/*
* Copyright (c) 2002-2009 "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.index.timeline;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Iterator;
import java.util.LinkedList;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.neo4j.graphdb.Node;
import org.neo4j.index.Neo4jTestCase;
public class TestTimeline extends Neo4jTestCase
{
private Timeline timeline;
@Before
public void setUpTimeline() throws Exception
{
Node node = graphDb().createNode();
timeline = new Timeline( "test_timeline", node, false, graphDb() );
}
@After
public void tearDownTimeline() throws Exception
{
timeline.delete();
}
private long getStamp()
{
long stamp = System.currentTimeMillis();
try
{
Thread.sleep( 20 );
}
catch ( InterruptedException e )
{
Thread.interrupted();
}
return stamp;
}
@Test
public void testTimelineBasic()
{
Node node1 = graphDb().createNode();
long stamp1 = getStamp();
node1.setProperty( "timestamp", stamp1 );
assertTrue( !timeline.getAllNodes().iterator().hasNext() );
assertTrue( !timeline.getAllNodesAfter( 0 ).iterator().hasNext() );
assertTrue( !timeline.getAllNodesBefore( 0 ).iterator().hasNext() );
assertTrue( !timeline.getAllNodesBetween( 0,
1 ).iterator().hasNext() );
assertTrue( timeline.getFirstNode() == null );
assertTrue( timeline.getLastNode() == null );
timeline.addNode( node1, stamp1 );
assertEquals( stamp1, timeline.getTimestampForNode( node1 ) );
Iterator<Node> itr = timeline.getAllNodes().iterator();
assertEquals( node1, itr.next() );
assertTrue( !itr.hasNext() );
itr = timeline.getAllNodesAfter( 0 ).iterator();
assertEquals( node1, itr.next() );
assertTrue( !itr.hasNext() );
itr = timeline.getAllNodesBefore( stamp1 + 1 ).iterator();
assertEquals( node1, itr.next() );
assertTrue( !itr.hasNext() );
itr = timeline.getAllNodesBetween( 0, stamp1 + 1 ).iterator();
assertEquals( node1, itr.next() );
assertTrue( !itr.hasNext() );
assertEquals( node1, timeline.getFirstNode() );
assertEquals( node1, timeline.getLastNode() );
timeline.removeNode( node1 );
assertTrue( !timeline.getAllNodes().iterator().hasNext() );
assertTrue( !timeline.getAllNodesAfter( 0 ).iterator().hasNext() );
assertTrue( !timeline.getAllNodesBefore( 0 ).iterator().hasNext() );
assertTrue( !timeline.getAllNodesBetween( 0,
1 ).iterator().hasNext() );
assertTrue( timeline.getFirstNode() == null );
assertTrue( timeline.getLastNode() == null );
timeline.addNode( node1, stamp1 );
assertEquals( stamp1, timeline.getTimestampForNode( node1 ) );
Node node2 = graphDb().createNode();
long stamp2 = getStamp();
node2.setProperty( "timestamp", stamp2 );
timeline.addNode( node2, stamp2 );
assertEquals( stamp2, timeline.getTimestampForNode( node2 ) );
itr = timeline.getAllNodes().iterator();
assertEquals( node1, itr.next() );
assertEquals( node2, itr.next() );
assertTrue( !itr.hasNext() );
itr = timeline.getAllNodesAfter( 0 ).iterator();
assertEquals( node1, itr.next() );
assertEquals( node2, itr.next() );
assertTrue( !itr.hasNext() );
itr = timeline.getAllNodesBefore( stamp2 + 1 ).iterator();
assertEquals( node1, itr.next() );
assertEquals( node2, itr.next() );
assertTrue( !itr.hasNext() );
itr = timeline.getAllNodesBetween( 0, stamp2 + 1 ).iterator();
assertEquals( node1, itr.next() );
assertEquals( node2, itr.next() );
assertTrue( !itr.hasNext() );
assertEquals( node1, timeline.getFirstNode() );
assertEquals( node2, timeline.getLastNode() );
itr = timeline.getAllNodes( null, null ).iterator();
assertEquals( node1, itr.next() );
assertEquals( node2, itr.next() );
assertTrue( !itr.hasNext() );
itr = timeline.getAllNodes( 0L, null ).iterator();
assertEquals( node1, itr.next() );
assertEquals( node2, itr.next() );
assertTrue( !itr.hasNext() );
itr = timeline.getAllNodes( null, stamp2 + 1 ).iterator();
assertEquals( node1, itr.next() );
assertEquals( node2, itr.next() );
assertTrue( !itr.hasNext() );
itr = timeline.getAllNodes( 0L, stamp2 + 1 ).iterator();
assertEquals( node1, itr.next() );
assertEquals( node2, itr.next() );
assertTrue( !itr.hasNext() );
timeline.removeNode( node1 );
timeline.removeNode( node2 );
assertTrue( !timeline.getAllNodes().iterator().hasNext() );
assertTrue( !timeline.getAllNodesAfter( 0 ).iterator().hasNext() );
assertTrue( !timeline.getAllNodesBefore( 0 ).iterator().hasNext() );
assertTrue( !timeline.getAllNodesBetween( 0,
1 ).iterator().hasNext() );
assertTrue( timeline.getFirstNode() == null );
assertTrue( timeline.getLastNode() == null );
timeline.addNode( node1, stamp1 );
timeline.addNode( node2, stamp2 );
Node node3 = graphDb().createNode();
long stamp3 = getStamp();
node3.setProperty( "timestamp", stamp3 );
timeline.addNode( node3, stamp3 );
itr = timeline.getAllNodes().iterator();
assertEquals( node1, itr.next() );
assertEquals( node2, itr.next() );
assertEquals( node3, itr.next() );
assertTrue( !itr.hasNext() );
itr = timeline.getAllNodesAfter( 0 ).iterator();
assertEquals( node1, itr.next() );
assertEquals( node2, itr.next() );
assertEquals( node3, itr.next() );
assertTrue( !itr.hasNext() );
itr = timeline.getAllNodesBefore( stamp3 + 1 ).iterator();
assertEquals( node1, itr.next() );
assertEquals( node2, itr.next() );
assertEquals( node3, itr.next() );
assertTrue( !itr.hasNext() );
itr = timeline.getAllNodesBetween( 0, stamp3 + 1 ).iterator();
assertEquals( node1, itr.next() );
assertEquals( node2, itr.next() );
assertEquals( node3, itr.next() );
assertTrue( !itr.hasNext() );
assertEquals( node1, timeline.getFirstNode() );
assertEquals( node3, timeline.getLastNode() );
itr = timeline.getAllNodesAfter( stamp1 ).iterator();
assertEquals( node2, itr.next() );
assertEquals( node3, itr.next() );
assertTrue( !itr.hasNext() );
itr = timeline.getAllNodesBefore( stamp3 ).iterator();
assertEquals( node1, itr.next() );
assertEquals( node2, itr.next() );
assertTrue( !itr.hasNext() );
itr = timeline.getAllNodesBetween( stamp1, stamp3 ).iterator();
assertEquals( node2, itr.next() );
assertTrue( !itr.hasNext() );
timeline.removeNode( node2 );
itr = timeline.getAllNodesAfter( stamp1 ).iterator();
assertEquals( node3, itr.next() );
assertTrue( !itr.hasNext() );
itr = timeline.getAllNodesBefore( stamp3 ).iterator();
assertEquals( node1, itr.next() );
assertTrue( !itr.hasNext() );
itr = timeline.getAllNodesBetween( stamp1, stamp3 ).iterator();
assertTrue( !itr.hasNext() );
assertEquals( node1, timeline.getFirstNode() );
assertEquals( node3, timeline.getLastNode() );
timeline.removeNode( node3 );
itr = timeline.getAllNodes().iterator();
assertEquals( node1, itr.next() );
assertTrue( !itr.hasNext() );
itr = timeline.getAllNodesAfter( 0 ).iterator();
assertEquals( node1, itr.next() );
assertTrue( !itr.hasNext() );
itr = timeline.getAllNodesBefore( stamp1 + 1 ).iterator();
assertEquals( node1, itr.next() );
assertTrue( !itr.hasNext() );
itr = timeline.getAllNodesBetween( 0, stamp1 + 1 ).iterator();
assertEquals( node1, itr.next() );
assertTrue( !itr.hasNext() );
assertEquals( node1, timeline.getFirstNode() );
assertEquals( node1, timeline.getLastNode() );
timeline.removeNode( node1 );
node1.delete();
node2.delete();
node3.delete();
}
@Test
public void testIllegalStuff()
{
Node node1 = graphDb().createNode();
long stamp1 = System.currentTimeMillis();
try
{
new Timeline( "blabla", null, true, graphDb() );
fail( "Null parameter should throw exception" );
}
catch ( IllegalArgumentException e ) { // good
}
try
{
new Timeline( "blabla", node1, false, null );
fail( "Null parameter should throw exception" );
}
catch ( IllegalArgumentException e ) { // good
}
node1.setProperty( "timestamp", stamp1 );
timeline.addNode( node1, stamp1 );
try
{
timeline.addNode( node1, stamp1 );
fail( "Re-adding node should throw exception" );
}
catch ( IllegalArgumentException e ) { // good
}
try
{
timeline.removeNode( timeline.getUnderlyingNode() );
fail( "Removing underlying node should throw exception" );
}
catch ( IllegalArgumentException e ) { // good
}
timeline.removeNode( node1 );
try
{
timeline.removeNode( node1 );
fail( "Removing non added node should throw exception" );
}
catch ( IllegalArgumentException e ) { // good
}
node1.delete();
}
@Test
public void testIndexedTimeline()
{
Node tlNode = graphDb().createNode();
Timeline timeline = new Timeline( "test", tlNode, true, graphDb() );
LinkedList<Node> after = new LinkedList<Node>();
LinkedList<Node> before = new LinkedList<Node>();
for ( long i = 1; i < 128; i++ )
{
Node node = graphDb().createNode();
timeline.addNode( node, i );
if ( i > 64 )
{
after.add( node );
}
else
{
before.add( node );
}
}
Iterable<Node> nodes = timeline.getAllNodesAfter( 64 );
while ( nodes.iterator().hasNext() )
{
Node nodeToRemove = nodes.iterator().next();
assert nodeToRemove.equals( after.removeFirst() );
timeline.removeNode( nodeToRemove );
nodes = timeline.getAllNodesBefore( 65 );
if ( nodes.iterator().hasNext() )
{
nodeToRemove = nodes.iterator().next();
assert nodeToRemove.equals( before.removeFirst() );
timeline.removeNode( nodeToRemove );
}
nodes = timeline.getAllNodesAfter( 64 );
}
nodes = timeline.getAllNodesBefore( 65 );
if ( nodes.iterator().hasNext() )
{
Node nodeToRemove = nodes.iterator().next();
assert nodeToRemove.equals( before.removeLast() );
timeline.removeNode( nodeToRemove );
}
assert !tlNode.getRelationships(
Timeline.RelTypes.TIMELINE_NEXT_ENTRY ).iterator().hasNext();
timeline.delete();
}
@Test
public void testIndexedTimeline2()
{
Node tlNode = graphDb().createNode();
Timeline timeline = new Timeline( "test", tlNode, true, graphDb() );
Node nodes[] = new Node[1000];
for ( int i = 0; i < 1000; i++ )
{
Node node = graphDb().createNode();
nodes[i] = node;
timeline.addNode( node, i );
}
for ( int i = 0; i < 1000; i++ )
{
Iterator<Node> itr = timeline.getNodes( i ).iterator();
assertEquals( nodes[i], itr.next() );
assertTrue( !itr.hasNext() );
}
timeline.delete();
for ( Node node : nodes )
{
node.delete();
}
}
@Test
public void testTimelineSameTimestamp()
{
Node tlNode = graphDb().createNode();
Timeline timeline = new Timeline( "test", tlNode, true, graphDb() );
Node node0 = graphDb().createNode();
Node node1_1 = graphDb().createNode();
Node node1_2 = graphDb().createNode();
Node node2 = graphDb().createNode();
timeline.addNode( node1_1, 1 );
timeline.addNode( node1_2, 1 );
timeline.addNode( node0, 0 );
timeline.addNode( node2, 2 );
Iterator<Node> itr = timeline.getAllNodes().iterator();
assertEquals( node0, itr.next() );
Node node1 = itr.next();
if ( node1.equals( node1_1 ) )
{
assertEquals( node1_2, itr.next() );
}
else if ( node1.equals( node1_2 ) )
{
assertEquals( node1_1, itr.next() );
}
else
{
fail( "should return node1_1 or node1_2" );
}
assertEquals( node2, itr.next() );
assertTrue( !itr.hasNext() );
itr = timeline.getNodes( 1 ).iterator();
node1 = itr.next();
if ( node1.equals( node1_1 ) )
{
assertEquals( node1_2, itr.next() );
}
else if ( node1.equals( node1_2 ) )
{
assertEquals( node1_1, itr.next() );
}
else
{
fail( "should return node1_1 or node1_2" );
}
node0.delete();
node1_1.delete();
node1_2.delete();
node2.delete();
timeline.delete();
}
@Test
public void testMultipleTimelines()
{
Node tlNode1 = graphDb().createNode();
Timeline timeline1 = new Timeline( "test1", tlNode1, true, graphDb() );
Node tlNode2 = graphDb().createNode();
Timeline timeline2 = new Timeline( "test2", tlNode2, true, graphDb() );
Node node1 = graphDb().createNode();
Node node2 = graphDb().createNode();
Node node3 = graphDb().createNode();
Node node4 = graphDb().createNode();
timeline1.addNode( node1, 1 );
timeline1.addNode( node2, 2 );
timeline2.addNode( node3, 1 );
timeline2.addNode( node4, 2 );
assertEquals( node2, timeline1.getLastNode() );
assertEquals( node4, timeline2.getLastNode() );
assertEquals( node1, timeline1.getFirstNode() );
assertEquals( node3, timeline2.getFirstNode() );
timeline1.addNode( node3, 3 );
Iterator<Node> itr = timeline1.getAllNodes().iterator();
assertEquals( node1, itr.next() );
assertEquals( node2, itr.next() );
assertEquals( node3, itr.next() );
assertTrue( !itr.hasNext() );
itr = timeline2.getAllNodes().iterator();
assertEquals( node3, itr.next() );
assertEquals( node4, itr.next() );
assertTrue( !itr.hasNext() );
timeline1.delete();
timeline2.delete();
node1.delete(); node2.delete(); node3.delete(); node4.delete();
}
@Test
public void testTimelineRemoveNode()
{
Node tlNode = graphDb().createNode();
Timeline indexedTimeline = new Timeline( "test", tlNode, true, graphDb() );
for ( long i = 1; i < 128; i++ )
{
Node node = graphDb().createNode();
indexedTimeline.addNode( node, i );
}
for ( Node node : indexedTimeline.getAllNodes() )
{
indexedTimeline.removeNode( node );
node.delete();
}
assertFalse( indexedTimeline.getAllNodes().iterator().hasNext() );
LinkedList<Node> nodes = new LinkedList<Node>();
for ( long i = 1; i < 128; i++ )
{
Node node = graphDb().createNode();
indexedTimeline.addNode( node, i );
nodes.add( node );
}
indexedTimeline.delete();
for ( Node node : nodes )
{
node.delete();
}
}
@Test
public void testDeleteTimeline()
{
Node tlNode = graphDb().createNode();
Timeline indexedTimeline = new Timeline( "test", tlNode, true, graphDb() );
Node[] nodes = new Node[20];
for ( int i = 1; i < nodes.length; i++ )
{
nodes[i] = graphDb().createNode();
indexedTimeline.addNode( nodes[i], i );
}
indexedTimeline.delete();
restartTx();
tlNode = graphDb().createNode();
indexedTimeline = new Timeline( "test", tlNode, true, graphDb() );
for ( int i = 1; i < nodes.length; i++ )
{
indexedTimeline.addNode( nodes[i], i );
}
indexedTimeline.delete();
restartTx();
for ( int i = 1; i < nodes.length; i++ )
{
nodes[i].delete();
}
}
}