Skip to content

Commit

Permalink
EntityCommandGrouperTest tests both node and relationship commands
Browse files Browse the repository at this point in the history
  • Loading branch information
tinwelint committed Apr 8, 2019
1 parent c492665 commit 1796166
Showing 1 changed file with 94 additions and 64 deletions.
Expand Up @@ -19,17 +19,22 @@
*/ */
package org.neo4j.kernel.impl.api.index; package org.neo4j.kernel.impl.api.index;


import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;


import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;


import org.neo4j.kernel.impl.store.record.NodeRecord; import org.neo4j.kernel.impl.store.record.NodeRecord;
import org.neo4j.kernel.impl.store.record.PrimitiveRecord;
import org.neo4j.kernel.impl.store.record.PropertyRecord; import org.neo4j.kernel.impl.store.record.PropertyRecord;
import org.neo4j.kernel.impl.store.record.RelationshipRecord;
import org.neo4j.kernel.impl.transaction.command.Command; import org.neo4j.kernel.impl.transaction.command.Command;
import org.neo4j.kernel.impl.transaction.command.Command.BaseCommand;
import org.neo4j.kernel.impl.transaction.command.Command.NodeCommand; import org.neo4j.kernel.impl.transaction.command.Command.NodeCommand;
import org.neo4j.kernel.impl.transaction.command.Command.RelationshipCommand;
import org.neo4j.test.extension.Inject; import org.neo4j.test.extension.Inject;
import org.neo4j.test.extension.RandomExtension; import org.neo4j.test.extension.RandomExtension;
import org.neo4j.test.rule.RandomRule; import org.neo4j.test.rule.RandomRule;
Expand All @@ -47,11 +52,12 @@ class EntityCommandGrouperTest


private long nextPropertyId; private long nextPropertyId;


@Test @ParameterizedTest
void shouldHandleEmptyList() @EnumSource( Factory.class )
void shouldHandleEmptyList( Factory factory )
{ {
// given // given
EntityCommandGrouper<NodeCommand> grouper = new EntityCommandGrouper<>( NodeCommand.class, 8 ); EntityCommandGrouper grouper = new EntityCommandGrouper<>( factory.command( 0 ).getClass(), 8 );


// when // when
EntityCommandGrouper.Cursor cursor = grouper.sortAndAccessGroups(); EntityCommandGrouper.Cursor cursor = grouper.sortAndAccessGroups();
Expand All @@ -61,96 +67,99 @@ void shouldHandleEmptyList()
assertFalse( hasNext ); assertFalse( hasNext );
} }


@Test @ParameterizedTest
void shouldSeeSingleGroupOfPropertiesWithNode() @EnumSource( Factory.class )
void shouldSeeSingleGroupOfPropertiesWithEntity( Factory factory )
{ {
// given // given
EntityCommandGrouper<NodeCommand> grouper = new EntityCommandGrouper<>( NodeCommand.class, 8 ); EntityCommandGrouper grouper = new EntityCommandGrouper<>( factory.command( 0 ).getClass(), 8 );
long nodeId = 1; long entityId = 1;
Command.PropertyCommand property1 = property( nodeId ); BaseCommand<? extends PrimitiveRecord> entity = factory.command( entityId );
Command.PropertyCommand property2 = property( nodeId ); Command.PropertyCommand property1 = property( entity.getAfter() );
NodeCommand node = node( nodeId ); Command.PropertyCommand property2 = property( entity.getAfter() );
grouper.add( property1 ); grouper.add( property1 );
grouper.add( property2 ); grouper.add( property2 );
grouper.add( node ); // <-- deliberately out-of-place grouper.add( entity ); // <-- deliberately out-of-place
EntityCommandGrouper.Cursor cursor = grouper.sortAndAccessGroups(); EntityCommandGrouper.Cursor cursor = grouper.sortAndAccessGroups();


// when/then // when/then
assertGroups( cursor, group( nodeId, node, property1, property2 ) ); assertGroups( cursor, group( entityId, entity, property1, property2 ) );
} }


@Test @ParameterizedTest
void shouldSeeSingleGroupOfPropertiesWithoutNode() @EnumSource( Factory.class )
void shouldSeeSingleGroupOfPropertiesWithoutEntity( Factory factory )
{ {
// given // given
EntityCommandGrouper<NodeCommand> grouper = new EntityCommandGrouper<>( NodeCommand.class, 8 ); EntityCommandGrouper grouper = new EntityCommandGrouper<>( factory.command( 0 ).getClass(), 8 );
long nodeId = 1; long entityId = 1;
Command.PropertyCommand property1 = property( nodeId ); BaseCommand<? extends PrimitiveRecord> entity = factory.command( entityId );
Command.PropertyCommand property2 = property( nodeId ); Command.PropertyCommand property1 = property( entity.getAfter() );
Command.PropertyCommand property2 = property( entity.getAfter() );
// intentionally DO NOT add the entity command
grouper.add( property1 ); grouper.add( property1 );
grouper.add( property2 ); grouper.add( property2 );
EntityCommandGrouper.Cursor cursor = grouper.sortAndAccessGroups(); EntityCommandGrouper.Cursor cursor = grouper.sortAndAccessGroups();


// when/then // when/then
assertGroups( cursor, group( nodeId, null, property1, property2 ) ); assertGroups( cursor, group( entityId, null, property1, property2 ) );
} }


@Test @ParameterizedTest
void shouldSeeMultipleGroupsSomeOfThemWithNode() @EnumSource( Factory.class )
void shouldSeeMultipleGroupsSomeOfThemWithEntity( Factory factory )
{ {
// given // given
EntityCommandGrouper<NodeCommand> grouper = new EntityCommandGrouper<>( NodeCommand.class, 64 ); EntityCommandGrouper grouper = new EntityCommandGrouper<>( factory.command( 0 ).getClass(), 64 );
Group[] groups = new Group[random.nextInt( 10, 30 )]; Group[] groups = new Group[random.nextInt( 10, 30 )];
for ( int nodeId = 0; nodeId < groups.length; nodeId++ ) for ( int entityId = 0; entityId < groups.length; entityId++ )
{ {
NodeCommand nodeCommand = random.nextBoolean() ? node( nodeId ) : null; BaseCommand entityCommand = random.nextBoolean() ? factory.command( entityId ) : null;
groups[nodeId] = new Group( nodeId, nodeCommand ); groups[entityId] = new Group( entityId, entityCommand );
if ( nodeCommand != null ) if ( entityCommand != null )
{ {
grouper.add( nodeCommand ); // <-- storage transaction logs are sorted such that node commands comes before property commands grouper.add( entityCommand ); // <-- storage transaction logs are sorted such that entity commands comes before property commands
} }
} }
int totalNumberOfProperties = random.nextInt( 10, 100 ); int totalNumberOfProperties = random.nextInt( 10, 100 );
for ( int i = 0; i < totalNumberOfProperties; i++ ) for ( int i = 0; i < totalNumberOfProperties; i++ )
{ {
int nodeId = random.nextInt( groups.length ); int entityId = random.nextInt( groups.length );
Command.PropertyCommand property = property( nodeId ); Command.PropertyCommand property = property( factory.command( entityId ).getAfter() );
groups[nodeId].addProperty( property ); groups[entityId].addProperty( property );
grouper.add( property ); grouper.add( property );
} }
// ^^^ OK so we've generated property commands for random nodes in random order, let's sort them // ^^^ OK so we've generated property commands for random entities in random order, let's sort them
EntityCommandGrouper.Cursor cursor = grouper.sortAndAccessGroups(); EntityCommandGrouper.Cursor cursor = grouper.sortAndAccessGroups();


// then // then
assertGroups( cursor, groups ); assertGroups( cursor, groups );
} }


@Test @ParameterizedTest
void shouldWorkOnADifferentSetOfCommandsAfterClear() @EnumSource( Factory.class )
void shouldWorkOnADifferentSetOfCommandsAfterClear( Factory factory )
{ {
// given // given
EntityCommandGrouper<NodeCommand> grouper = new EntityCommandGrouper<>( NodeCommand.class, 16 ); EntityCommandGrouper grouper = new EntityCommandGrouper<>( factory.command( 0 ).getClass(), 16 );
grouper.add( node( 0 ) ); BaseCommand<? extends PrimitiveRecord> entity0 = factory.command( 0 );
grouper.add( node( 1 ) ); BaseCommand<? extends PrimitiveRecord> entity1 = factory.command( 1 );
grouper.add( property( 0 ) ); grouper.add( entity0 );
grouper.add( property( 1 ) ); grouper.add( entity1 );
grouper.add( property( entity0.getAfter() ) );
grouper.add( property( entity1.getAfter() ) );
grouper.clear(); grouper.clear();


// when // when
Command.NodeCommand node2 = node( 2 ); BaseCommand<? extends PrimitiveRecord> entity2 = factory.command( 2 );
Command.PropertyCommand node2Property = property( 2 ); Command.PropertyCommand entityProperty = property( entity2.getAfter() );
Command.NodeCommand node3 = node( 3 ); BaseCommand<? extends PrimitiveRecord> entity3 = factory.command( 3 );
grouper.add( node2 ); grouper.add( entity2 );
grouper.add( node2Property ); grouper.add( entityProperty );
grouper.add( node3 ); grouper.add( entity3 );


// then // then
assertGroups( grouper.sortAndAccessGroups(), group( node2.getKey(), node2, node2Property ), group( node3.getKey(), node3 ) ); assertGroups( grouper.sortAndAccessGroups(), group( entity2.getKey(), entity2, entityProperty ), group( entity3.getKey(), entity3 ) );
}

private NodeCommand node( long nodeId )
{
return new NodeCommand( new NodeRecord( nodeId ), new NodeRecord( nodeId ) );
} }


private void assertGroups( EntityCommandGrouper.Cursor cursor, Group... groups ) private void assertGroups( EntityCommandGrouper.Cursor cursor, Group... groups )
Expand All @@ -167,28 +176,27 @@ private void assertGroups( EntityCommandGrouper.Cursor cursor, Group... groups )
assertFalse( cursor.nextEntity() ); assertFalse( cursor.nextEntity() );
} }


private Group group( long nodeId, NodeCommand nodeCommand, Command.PropertyCommand... properties ) private Group group( long entityId, BaseCommand<? extends PrimitiveRecord> entityCommand, Command.PropertyCommand... properties )
{ {
return new Group( nodeId, nodeCommand, properties ); return new Group( entityId, entityCommand, properties );
} }


private Command.PropertyCommand property( long nodeId ) private Command.PropertyCommand property( PrimitiveRecord owner )
{ {
long propertyId = nextPropertyId++; long propertyId = nextPropertyId++;
NodeRecord nodeRecord = new NodeRecord( nodeId ); return new Command.PropertyCommand( new PropertyRecord( propertyId, owner ), new PropertyRecord( propertyId, owner ) );
return new Command.PropertyCommand( new PropertyRecord( propertyId, nodeRecord ), new PropertyRecord( propertyId, nodeRecord ) );
} }


private static class Group private static class Group
{ {
private final long nodeId; private final long entityId;
private final NodeCommand nodeCommand; private final Command entityCommand;
private final Set<Command.PropertyCommand> properties = new HashSet<>(); private final Set<Command.PropertyCommand> properties = new HashSet<>();


Group( long nodeId, NodeCommand nodeCommand, Command.PropertyCommand... properties ) Group( long entityId, Command entityCommand, Command.PropertyCommand... properties )
{ {
this.nodeId = nodeId; this.entityId = entityId;
this.nodeCommand = nodeCommand; this.entityCommand = entityCommand;
this.properties.addAll( Arrays.asList( properties ) ); this.properties.addAll( Arrays.asList( properties ) );
} }


Expand All @@ -199,8 +207,8 @@ void addProperty( Command.PropertyCommand property )


void assertGroup( EntityCommandGrouper.Cursor cursor ) void assertGroup( EntityCommandGrouper.Cursor cursor )
{ {
assertEquals( nodeId, cursor.currentEntityId() ); assertEquals( entityId, cursor.currentEntityId() );
assertSame( nodeCommand, cursor.currentEntityCommand() ); assertSame( entityCommand, cursor.currentEntityCommand() );
Set<Command.PropertyCommand> fromGrouper = new HashSet<>(); Set<Command.PropertyCommand> fromGrouper = new HashSet<>();
while ( true ) while ( true )
{ {
Expand All @@ -216,7 +224,29 @@ void assertGroup( EntityCommandGrouper.Cursor cursor )


boolean isEmpty() boolean isEmpty()
{ {
return nodeCommand == null && properties.isEmpty(); return entityCommand == null && properties.isEmpty();
} }
} }

private enum Factory
{
NODE
{
@Override
NodeCommand command( long value )
{
return new NodeCommand( new NodeRecord( value ), new NodeRecord( value ) );
}
},
RELATIONSHIP
{
@Override
RelationshipCommand command( long value )
{
return new RelationshipCommand( new RelationshipRecord( value ), new RelationshipRecord( value ) );
}
};

abstract BaseCommand<? extends PrimitiveRecord> command( long id );
}
} }

0 comments on commit 1796166

Please sign in to comment.