/**
* Copyright (c) 2002-2012 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.consistency.checking.full;
import java.io.StringWriter;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.neo4j.consistency.RecordType;
import org.neo4j.consistency.report.ConsistencySummaryStatistics;
import org.neo4j.graphdb.DynamicRelationshipType;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.helpers.UTF8;
import org.neo4j.helpers.progress.ProgressMonitorFactory;
import org.neo4j.kernel.impl.nioneo.store.DynamicRecord;
import org.neo4j.kernel.impl.nioneo.store.NeoStoreRecord;
import org.neo4j.kernel.impl.nioneo.store.NodeRecord;
import org.neo4j.kernel.impl.nioneo.store.PropertyBlock;
import org.neo4j.kernel.impl.nioneo.store.PropertyRecord;
import org.neo4j.kernel.impl.nioneo.store.PropertyType;
import org.neo4j.kernel.impl.nioneo.store.RelationshipRecord;
import org.neo4j.kernel.impl.nioneo.store.RelationshipTypeRecord;
import org.neo4j.kernel.impl.nioneo.store.StoreAccess;
import org.neo4j.kernel.impl.util.StringLogger;
import org.neo4j.test.GraphStoreFixture;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.neo4j.consistency.checking.full.ExecutionOrderIntegrationTest.config;
import static org.neo4j.test.Property.property;
import static org.neo4j.test.Property.set;
public class FullCheckIntegrationTest
{
@Rule
public final GraphStoreFixture fixture = new GraphStoreFixture()
{
@Override
protected void generateInitialData( GraphDatabaseService graphDb )
{
org.neo4j.graphdb.Transaction tx = graphDb.beginTx();
try
{
Node node1 = set( graphDb.createNode() );
Node node2 = set( graphDb.createNode(), property( "key", "value" ) );
node1.createRelationshipTo( node2, DynamicRelationshipType.withName( "C" ) );
tx.success();
}
finally
{
tx.finish();
}
}
};
private final StringWriter log = new StringWriter();
private ConsistencySummaryStatistics check() throws ConsistencyCheckIncompleteException
{
return check( fixture.storeAccess() );
}
private ConsistencySummaryStatistics check( StoreAccess access ) throws ConsistencyCheckIncompleteException
{
FullCheck checker = new FullCheck( config( TaskExecutionOrder.SINGLE_THREADED ), ProgressMonitorFactory.NONE );
return checker.execute( access, StringLogger.wrap( log ) );
}
private void verifyInconsistency( RecordType recordType, ConsistencySummaryStatistics stats )
{
int count = stats.getInconsistencyCountForRecordType( recordType );
assertTrue( "Expected inconsistencies for records of type " + recordType, count > 0 );
assertEquals( "Expected only inconsistencies of type " + recordType + ", got:\n" + log,
count, stats.getTotalInconsistencyCount() );
}
@Test
public void shouldCheckConsistencyOfAConsistentStore() throws Exception
{
// when
ConsistencySummaryStatistics result = check();
// then
assertEquals( 0, result.getTotalInconsistencyCount() );
}
@Test
@Ignore("Support for checking NeoStore needs to be added")
public void shouldReportNeoStoreInconsistencies() throws Exception
{
// given
fixture.apply( new GraphStoreFixture.Transaction()
{
@Override
protected void transactionData( GraphStoreFixture.TransactionDataBuilder tx,
GraphStoreFixture.IdGenerator next )
{
NeoStoreRecord record = new NeoStoreRecord();
record.setNextProp( next.property() );
tx.update( record );
// We get exceptions when only the above happens in a transaction...
tx.create( new NodeRecord( next.node(), -1, -1 ) );
}
} );
// when
ConsistencySummaryStatistics stats = check();
// then
verifyInconsistency( RecordType.NEO_STORE, stats );
}
@Test
public void shouldReportNodeInconsistencies() throws Exception
{
// given
fixture.apply( new GraphStoreFixture.Transaction()
{
@Override
protected void transactionData( GraphStoreFixture.TransactionDataBuilder tx,
GraphStoreFixture.IdGenerator next )
{
tx.create( new NodeRecord( next.node(), next.relationship(), -1 ) );
}
} );
// when
ConsistencySummaryStatistics stats = check();
// then
verifyInconsistency( RecordType.NODE, stats );
}
@Test
public void shouldReportRelationshipInconsistencies() throws Exception
{
// given
fixture.apply( new GraphStoreFixture.Transaction()
{
@Override
protected void transactionData( GraphStoreFixture.TransactionDataBuilder tx,
GraphStoreFixture.IdGenerator next )
{
tx.create( new RelationshipRecord( next.relationship(), 1, 2, 0 ) );
}
} );
// when
ConsistencySummaryStatistics stats = check();
// then
verifyInconsistency( RecordType.RELATIONSHIP, stats );
}
@Test
public void shouldReportPropertyInconsistencies() throws Exception
{
// given
fixture.apply( new GraphStoreFixture.Transaction()
{
@Override
protected void transactionData( GraphStoreFixture.TransactionDataBuilder tx,
GraphStoreFixture.IdGenerator next )
{
PropertyRecord property = new PropertyRecord( next.property() );
property.setPrevProp( next.property() );
PropertyBlock block = new PropertyBlock();
block.setSingleBlock( 1 | (((long) PropertyType.INT.intValue()) << 24) | (666 << 28) );
property.addPropertyBlock( block );
tx.create( property );
}
} );
// when
ConsistencySummaryStatistics stats = check();
// then
verifyInconsistency( RecordType.PROPERTY, stats );
}
@Test
public void shouldReportStringPropertyInconsistencies() throws Exception
{
// given
fixture.apply( new GraphStoreFixture.Transaction()
{
@Override
protected void transactionData( GraphStoreFixture.TransactionDataBuilder tx,
GraphStoreFixture.IdGenerator next )
{
DynamicRecord string = new DynamicRecord( next.stringProperty() );
string.setInUse( true );
string.setCreated();
string.setType( PropertyType.STRING.intValue() );
string.setNextBlock( next.stringProperty() );
string.setData( UTF8.encode( "hello world" ) );
PropertyBlock block = new PropertyBlock();
block.setSingleBlock( (((long) PropertyType.STRING.intValue()) << 24) | (string.getId() << 28) );
block.addValueRecord( string );
PropertyRecord property = new PropertyRecord( next.property() );
property.addPropertyBlock( block );
tx.create( property );
}
} );
// when
ConsistencySummaryStatistics stats = check();
// then
verifyInconsistency( RecordType.STRING_PROPERTY, stats );
}
@Test
public void shouldReportArrayPropertyInconsistencies() throws Exception
{
// given
fixture.apply( new GraphStoreFixture.Transaction()
{
@Override
protected void transactionData( GraphStoreFixture.TransactionDataBuilder tx,
GraphStoreFixture.IdGenerator next )
{
DynamicRecord array = new DynamicRecord( next.arrayProperty() );
array.setInUse( true );
array.setCreated();
array.setType( PropertyType.ARRAY.intValue() );
array.setNextBlock( next.arrayProperty() );
array.setData( UTF8.encode( "hello world" ) );
PropertyBlock block = new PropertyBlock();
block.setSingleBlock( (((long) PropertyType.ARRAY.intValue()) << 24) | (array.getId() << 28) );
block.addValueRecord( array );
PropertyRecord property = new PropertyRecord( next.property() );
property.addPropertyBlock( block );
tx.create( property );
}
} );
// when
ConsistencySummaryStatistics stats = check();
// then
verifyInconsistency( RecordType.ARRAY_PROPERTY, stats );
}
@Test
public void shouldReportRelationshipLabelNameInconsistencies() throws Exception
{
// given
final Reference<Integer> inconsistentName = new Reference<Integer>();
fixture.apply( new GraphStoreFixture.Transaction()
{
@Override
protected void transactionData( GraphStoreFixture.TransactionDataBuilder tx,
GraphStoreFixture.IdGenerator next )
{
inconsistentName.set( next.relationshipType() );
tx.relationshipType( inconsistentName.get(), "FOO" );
}
} );
StoreAccess access = fixture.storeAccess();
DynamicRecord record = access.getTypeNameStore().forceGetRecord( inconsistentName.get() );
record.setNextBlock( record.getId() );
access.getTypeNameStore().updateRecord( record );
// when
ConsistencySummaryStatistics stats = check( access );
// then
verifyInconsistency( RecordType.RELATIONSHIP_LABEL_NAME, stats );
}
@Test
public void shouldReportPropertyKeyNameInconsistencies() throws Exception
{
// given
final Reference<Integer> inconsistentName = new Reference<Integer>();
fixture.apply( new GraphStoreFixture.Transaction()
{
@Override
protected void transactionData( GraphStoreFixture.TransactionDataBuilder tx,
GraphStoreFixture.IdGenerator next )
{
inconsistentName.set( next.propertyKey() );
tx.propertyKey( inconsistentName.get(), "FOO" );
}
} );
StoreAccess access = fixture.storeAccess();
DynamicRecord record = access.getPropertyKeyStore().forceGetRecord( inconsistentName.get() );
record.setNextBlock( record.getId() );
access.getPropertyKeyStore().updateRecord( record );
// when
ConsistencySummaryStatistics stats = check( access );
// then
verifyInconsistency( RecordType.PROPERTY_KEY_NAME, stats );
}
@Test
public void shouldReportRelationshipLabelInconsistencies() throws Exception
{
// given
StoreAccess access = fixture.storeAccess();
RelationshipTypeRecord record = access.getRelationshipTypeStore().forceGetRecord( 1 );
record.setNameId( 20 );
record.setInUse( true );
access.getRelationshipTypeStore().updateRecord( record );
// when
ConsistencySummaryStatistics stats = check( access );
// then
verifyInconsistency( RecordType.RELATIONSHIP_LABEL, stats );
}
@Test
public void shouldReportPropertyKeyInconsistencies() throws Exception
{
// given
final Reference<Integer> inconsistentKey = new Reference<Integer>();
fixture.apply( new GraphStoreFixture.Transaction()
{
@Override
protected void transactionData( GraphStoreFixture.TransactionDataBuilder tx,
GraphStoreFixture.IdGenerator next )
{
inconsistentKey.set( next.propertyKey() );
tx.propertyKey( inconsistentKey.get(), "FOO" );
}
} );
StoreAccess access = fixture.storeAccess();
DynamicRecord record = access.getPropertyKeyStore().forceGetRecord( inconsistentKey.get() );
record.setInUse( false );
access.getPropertyKeyStore().updateRecord( record );
// when
ConsistencySummaryStatistics stats = check( access );
// then
verifyInconsistency( RecordType.PROPERTY_KEY, stats );
}
private static class Reference<T>
{
private T value;
void set(T value)
{
this.value = value;
}
T get()
{
return value;
}
@Override
public String toString()
{
return String.valueOf( value );
}
}
}