/**
* 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.incremental;
import java.io.IOException;
import java.util.Map;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.neo4j.consistency.ConsistencyCheckingError;
import org.neo4j.consistency.RecordType;
import org.neo4j.consistency.checking.incremental.intercept.VerifyingTransactionInterceptorProvider;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.factory.GraphDatabaseSettings;
import org.neo4j.helpers.UTF8;
import org.neo4j.kernel.impl.nioneo.store.DynamicRecord;
import org.neo4j.kernel.impl.nioneo.store.LongerShortString;
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.transaction.xaframework.TransactionInterceptorProvider;
import org.neo4j.test.GraphStoreFixture;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.neo4j.graphdb.DynamicRelationshipType.withName;
import static org.neo4j.test.Property.property;
import static org.neo4j.test.Property.set;
public class IncrementalCheckIntegrationTest
{
@Test
@Ignore("Support for checking NeoStore needs to be added")
public void shouldReportNeoStoreInconsistencies() throws Exception
{
verifyInconsistencyReported( RecordType.NEO_STORE, 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 ) );
}
} );
}
@Test
public void shouldReportNodeInconsistency() throws Exception
{
verifyInconsistencyReported( RecordType.NODE, new GraphStoreFixture.Transaction()
{
@Override
protected void transactionData( GraphStoreFixture.TransactionDataBuilder tx,
GraphStoreFixture.IdGenerator next )
{
tx.create( new NodeRecord( next.node(), next.relationship(), -1 ) );
}
} );
}
@Test
public void shouldReportRelationshipInconsistency() throws Exception
{
verifyInconsistencyReported( RecordType.RELATIONSHIP, new GraphStoreFixture.Transaction()
{
@Override
protected void transactionData( GraphStoreFixture.TransactionDataBuilder tx,
GraphStoreFixture.IdGenerator next )
{
long node = next.node();
tx.create( new RelationshipRecord( next.relationship(), node, node, 0 ) );
}
} );
}
@Test
public void shouldReportPropertyInconsistency() throws Exception
{
verifyInconsistencyReported( RecordType.PROPERTY, new GraphStoreFixture.Transaction()
{
@Override
protected void transactionData( GraphStoreFixture.TransactionDataBuilder tx,
GraphStoreFixture.IdGenerator next )
{
PropertyRecord property = new PropertyRecord( next.property() );
property.setPrevProp( next.property() );
property.setNodeId( 1 );
PropertyBlock block = new PropertyBlock();
block.setSingleBlock( (((long) PropertyType.INT.intValue()) << 24) | (666 << 28) );
property.addPropertyBlock( block );
tx.create( property );
}
} );
}
@Test
public void shouldReportStringPropertyInconsistency() throws Exception
{
verifyInconsistencyReported( RecordType.STRING_PROPERTY, 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 );
NodeRecord node = new NodeRecord( next.node(), -1, property.getId() );
property.setNodeId( node.getId() );
tx.create( node );
tx.create( property );
}
} );
}
@Test
public void shouldReportArrayPropertyInconsistency() throws Exception
{
verifyInconsistencyReported( RecordType.ARRAY_PROPERTY, 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 );
NodeRecord node = new NodeRecord( next.node(), -1, property.getId() );
property.setNodeId( node.getId() );
tx.create( node );
tx.create( property );
}
} );
}
private static String LONG_STRING, LONG_SHORT_STRING;
static
{
StringBuilder longString = new StringBuilder();
String longShortString = "";
for ( int i = 0; LongerShortString.encode( 0, longString.toString(), new PropertyBlock(),
PropertyType.getPayloadSize() ); i++ )
{
longShortString = longString.toString();
longString.append( 'a' + (i % ('z' - 'a')) );
}
LONG_SHORT_STRING = longShortString;
LONG_STRING = longString.toString();
}
@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(),
property( "long short short", LONG_SHORT_STRING ) );
Node node2 = set( graphDb.createNode(),
property( "long string", LONG_STRING ) );
Node node3 = set( graphDb.createNode(),
property( "one", "1" ),
property( "two", "2" ),
property( "three", "3" ),
property( "four", "4" ),
property( "five", "5" ) );
Node node4 = set( graphDb.createNode(),
property( "name", "Leeloo Dallas" ) );
Node node5 = set( graphDb.createNode(),
property( "payload", LONG_SHORT_STRING ),
property( "more", LONG_STRING ) );
Node node6 = set( graphDb.createNode() );
set( node1.createRelationshipTo( node2, withName( "WHEEL" ) ) );
set( node2.createRelationshipTo( node3, withName( "WHEEL" ) ) );
set( node3.createRelationshipTo( node4, withName( "WHEEL" ) ) );
set( node4.createRelationshipTo( node5, withName( "WHEEL" ) ) );
set( node5.createRelationshipTo( node1, withName( "WHEEL" ) ) );
set( node6.createRelationshipTo( node1, withName( "STAR" ) ) );
set( node6.createRelationshipTo( node2, withName( "STAR" ) ) );
set( node6.createRelationshipTo( node3, withName( "STAR" ) ) );
set( node6.createRelationshipTo( node4, withName( "STAR" ) ) );
set( node6.createRelationshipTo( node5, withName( "STAR" ) ) );
tx.success();
}
finally
{
tx.finish();
}
}
@Override
protected Map<String, String> configuration( boolean initialData )
{
Map<String, String> config = super.configuration( initialData );
if ( !initialData )
{
config.put( GraphDatabaseSettings.intercept_deserialized_transactions.name(), "true" );
config.put( GraphDatabaseSettings.intercept_committing_transactions.name(), "true" );
config.put( TransactionInterceptorProvider.class.getSimpleName() + "." +
VerifyingTransactionInterceptorProvider.NAME, "true" );
}
return config;
}
};
private void verifyInconsistencyReported( RecordType recordType,
GraphStoreFixture.Transaction inconsistentTransaction )
throws IOException
{
// when
try
{
fixture.apply( inconsistentTransaction );
fail( "should have thrown error" );
}
// then
catch ( ConsistencyCheckingError expected )
{
int count = expected.getInconsistencyCountForRecordType( recordType );
int total = expected.getTotalInconsistencyCount();
String summary = expected.getMessage().replace( "\n", "\n\t" );
assertTrue( "Expected failures for " + recordType + ", got " + summary, count > 0 );
assertEquals( "Didn't expect failures for any other type than " + recordType + ", got " + summary,
count, total );
}
}
}