/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Dec 11, 2006
*/
package com.bigdata.htree;
import com.bigdata.rawstore.IRawStore;
import com.bigdata.rawstore.SimpleMemoryRawStore;
/**
* Test suite for iterators that visit only dirty nodes or leaves. This suite
* relies on (and to some extent validates) both node and leaf IO and
* copy-on-write mechanisms.
*
* @see DirectoryPage#childIterator(boolean)
* @see AbstractPage#postOrderNodeIterator(boolean, boolean)
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*
* TODO Extend this test suite to verify that remove() on the htree
* uses the copy-on-write pattern correctly.
*/
public class TestDirtyIterators extends AbstractHTreeTestCase {
/**
*
*/
public TestDirtyIterators() {
}
/**
* @param name
*/
public TestDirtyIterators(String name) {
super(name);
}
/**
* Test ability to visit the direct dirty children of a node. For this test
* we only verify that the dirty child iterator will visit the same children
* as the normal child iterator. This is true since we never evict a node
* onto the store during this test (the index is setup to throw an exception
* if the tree attempts a node eviction).
*/
public void test_dirtyChildIterator01() {
final IRawStore store = new SimpleMemoryRawStore();
try {
final HTree htree = getHTree(store, 2/* addressBits */);
final byte[] k1 = new byte[]{0x01};
final byte[] k2 = new byte[]{0x02};
final byte[] k3 = new byte[]{0x03};
final byte[] k4 = new byte[]{0x04};
final byte[] k5 = new byte[]{0x05};
final byte[] v1 = new byte[]{0x01};
final byte[] v2 = new byte[]{0x02};
final byte[] v3 = new byte[]{0x03};
final byte[] v4 = new byte[]{0x04};
final byte[] v5 = new byte[]{0x05};
{
// verify initial structure.
final DirectoryPage root = htree.root;
final BucketPage a = (BucketPage) root.getChild(0);
assertTrue(a == root.getChild(1));
assertTrue(a == root.getChild(2));
assertTrue(a == root.getChild(3));
// direct child iterator.
assertSameIterator(new AbstractPage[] { a },
root.childIterator(false/* dirtyOnly */));
assertSameIterator(new AbstractPage[] { a },
root.childIterator(true/* dirtyOnly */));
// empty tree visits the sole bucket page followed by the root.
assertSameIterator(new AbstractPage[] { a, root },
htree.root.postOrderNodeIterator(false));
assertSameIterator(new AbstractPage[] { a, root },
htree.root.postOrderNodeIterator(true));
}
{
htree.insert(k1, v1);
htree.insert(k2, v2);
htree.insert(k3, v3);
htree.insert(k4, v4);
if (log.isInfoEnabled())
log.info("After insert: " + htree.PP());
// There should be only one directory page and one bucket page.
assertEquals("nnodes", 1, htree.getNodeCount());
assertEquals("nleaves", 1, htree.getLeafCount());
assertEquals("nentries", 4, htree.getEntryCount());
final BucketPage a = (BucketPage) htree.root.getChild(0);
assertSameIterator(new AbstractPage[] { a },
htree.root.childIterator(false/* onlyDirty */));
assertSameIterator(new AbstractPage[] { a },
htree.root.childIterator(true/* onlyDirty */));
}
/**
* Insert another tuple to force some splits.
*
* The expected post-condition is:
*
* <pre>
* #nodes=3, #leaves=3, #entries=5
* D#88 [2] (D#62,-,-,-)
* D#62 [2] (D#65,-,-,-)
* D#65 [2] (B#99,B#35,-,-)
* B#99 [2] ([1](00000001),[2](00000010),[3](00000011),-)
* B#35 [2] ([4](00000100),[5](00000101),-,-)
* </pre>
*/
{
htree.insert(k5, v5);
if (log.isInfoEnabled())
log.info("After another insert: " + htree.PP());
assertEquals("nnodes", 3, htree.getNodeCount());
assertEquals("nleaves", 2, htree.getLeafCount());
assertEquals("nentries", 5, htree.getEntryCount());
final DirectoryPage a = (DirectoryPage) htree.root.getChild(0);
final BucketPage b = (BucketPage) htree.root.getChild(1);
final BucketPage c = (BucketPage) htree.root.getChild(2);
assertTrue(c == (BucketPage) htree.root.getChild(3));
assertSameIterator(new AbstractPage[] { a, b, c },
htree.root.childIterator(false/* onlyDirty */));
assertSameIterator(new AbstractPage[] { a, b, c},
htree.root.childIterator(true/* onlyDirty */));
}
} finally {
store.destroy();
}
}
/**
* Test ability to visit the direct dirty children of a node. This test
* works by explicitly writing out either the root node or a leaf and
* verifying that the dirty children iterator correctly visits only those
* children that should be marked as dirty after the others have been
* written onto the store. Note that this does not force the eviction of
* nodes or leaves but rather requests that the are written out directly.
* Whenever we make an immutable node or leaf mutable using copy-on-write,
* we wind up with a new reference for that node or leaf and update the
* variables in the test appropriately.
*/
public void test_dirtyChildIterator02() {
final byte[] k1 = new byte[]{0x01};
final byte[] k2 = new byte[]{0x02};
final byte[] k3 = new byte[]{0x03};
final byte[] k4 = new byte[]{0x04};
final byte[] k5 = new byte[]{0x05};
final byte[] v1 = new byte[]{0x01};
final byte[] v2 = new byte[] { 0x02 };
final byte[] v3 = new byte[] { 0x03 };
final byte[] v4 = new byte[] { 0x04 };
final byte[] v5 = new byte[] { 0x05 };
final IRawStore store = new SimpleMemoryRawStore();
try {
final HTree htree = getHTree(store, 2/* addressBits */);
/**
* Insert another tuple to force some splits. The expected
* post-condition is:
*
* <pre>
* #nodes=3, #leaves=3, #entries=5
* D#88 [2] (D#62,B#80,B#20,B#20) // root
* D#62 [2] (D#65,B#94,B#92,B#92) // a
* D#65 [2] (B#99,B#35,B#65,B#65) // d
* B#99 [2] ([1](00000001),[2](00000010),[3](00000011),-) // g
* B#35 [2] ([4](00000100),[5](00000101),-,-) // h
* B#65 [1] (-,-,-,-) // i
* B#94 [2] (-,-,-,-) // e
* B#92 [1] (-,-,-,-) // f
* B#80 [2] (-,-,-,-) // b
* B#20 [1] (-,-,-,-) // c
* </pre>
*/
htree.insert(k1, v1);
htree.insert(k2, v2);
htree.insert(k3, v3);
htree.insert(k4, v4);
htree.insert(k5, v5);
{
if (log.isInfoEnabled())
log.info("After initial inserts: " + htree.PP());
assertEquals("nnodes", 3, htree.getNodeCount());
assertEquals("nleaves", 2, htree.getLeafCount());
assertEquals("nentries", 5, htree.getEntryCount());
final DirectoryPage root = (DirectoryPage) htree.root;
final DirectoryPage a = (DirectoryPage) htree.root.getChild(0);
final BucketPage b = (BucketPage) htree.root.getChild(1);
final BucketPage c = (BucketPage) htree.root.getChild(2);
assertTrue(c == (BucketPage) htree.root.getChild(3));
final DirectoryPage d = (DirectoryPage) a.getChild(0);
final BucketPage e = (BucketPage) a.getChild(1);
final BucketPage f = (BucketPage) a.getChild(2);
assertTrue(f == (BucketPage) a.getChild(3));
final BucketPage g = (BucketPage) d.getChild(0);
final BucketPage h = (BucketPage) d.getChild(1);
final BucketPage i = (BucketPage) d.getChild(2);
assertTrue(i == (BucketPage) d.getChild(3));
/*
* Verify that iterator visits all direct children (all direct
* children are dirty).
*/
// root's children
assertSameIterator(new AbstractPage[] { a, b, c },
htree.root.childIterator(false/* onlyDirty */));
assertSameIterator(new AbstractPage[] { a, b, c },
htree.root.childIterator(true/* onlyDirty */));
// a's children
assertSameIterator(new AbstractPage[] { d, e, f },
a.childIterator(false/* onlyDirty */));
assertSameIterator(new AbstractPage[] { d, e, f },
a.childIterator(true/* onlyDirty */));
// d's children
assertSameIterator(new AbstractPage[] { g, h, i },
d.childIterator(false/* onlyDirty */));
assertSameIterator(new AbstractPage[] { g, h, i },
d.childIterator(true/* onlyDirty */));
// verify the post-order iterator (all pages are dirty).
assertSameIterator(new AbstractPage[] { g, h, i, d, e, f, a, b,
c, root },
htree.root.postOrderNodeIterator(false/* dirtyOnly */));
assertSameIterator(new AbstractPage[] { g, h, i, d, e, f, a, b,
c, root },
htree.root.postOrderNodeIterator(true/* dirtyOnly */));
/*
* write (b) onto the store and verify that it is no longer
* visited by the dirty child iterator for its parent.
*/
htree.writeNodeRecursive(b);
assertFalse(b.isDirty());
assertTrue(b.isPersistent());
// direct children
assertSameIterator(new AbstractPage[] { a, b, c },
htree.root.childIterator(false/* onlyDirty */));
assertSameIterator(new AbstractPage[] { a, c },
htree.root.childIterator(true/* onlyDirty */));
// verify the post-order iterator.
assertSameIterator(new AbstractPage[] { g, h, i, d, e, f, a, b,
c, root },
htree.root.postOrderNodeIterator(false/* dirtyOnly */));
assertSameIterator(new AbstractPage[] { g, h, i, d, e, f, a, c,
root },
htree.root.postOrderNodeIterator(true/* dirtyOnly */));
/*
* write (c) onto the store and verify that it is no longer
* visited by the dirty child iterator for its parent.
*/
htree.writeNodeRecursive(c);
assertFalse(c.isDirty());
assertTrue(c.isPersistent());
// direct children.
assertSameIterator(new AbstractPage[] { a, b, c },
htree.root.childIterator(false/* onlyDirty */));
assertSameIterator(new AbstractPage[] { a },
htree.root.childIterator(true/* onlyDirty */));
// verify the post-order iterator.
assertSameIterator(new AbstractPage[] { g, h, i, d, e, f, a, b,
c, root },
htree.root.postOrderNodeIterator(false/* dirtyOnly */));
assertSameIterator(new AbstractPage[] { g, h, i, d, e, f, a,
root },
htree.root.postOrderNodeIterator(true/* dirtyOnly */));
/*
* write (d) onto the store and verify that it is no longer
* visited by the dirty child iterator for its parent.
*/
// before we write out (d).
assertTrue(d.isDirty());
assertFalse(d.isPersistent());
// verify direct children.
assertSameIterator(new AbstractPage[] { g, h, i },
d.childIterator(false/* onlyDirty */));
assertSameIterator(new AbstractPage[] { g, h, i },
d.childIterator(true/* onlyDirty */));
htree.writeNodeRecursive(d); // write page (recursive)
assertFalse(d.isDirty());
assertTrue(d.isPersistent());
// verify direct children.
assertSameIterator(new AbstractPage[] { d, e, f },
a.childIterator(false/* onlyDirty */));
assertSameIterator(new AbstractPage[] { e, f },
a.childIterator(true/* onlyDirty */));
// also verify that none of the children of (d) are visited by
// the dirty iterator for (d).
assertSameIterator(new AbstractPage[] { g, h, i },
d.childIterator(false/* onlyDirty */));
assertSameIterator(new AbstractPage[] {},
d.childIterator(true/* onlyDirty */));
// verify the post-order iterator.
assertSameIterator(new AbstractPage[] { g, h, i, d, e, f, a, b,
c, root },
htree.root.postOrderNodeIterator(false/* dirtyOnly */));
assertSameIterator(new AbstractPage[] { e, f, a, root },
htree.root.postOrderNodeIterator(true/* dirtyOnly */));
/*
* write (f) onto the store and verify that it is no longer
* visited by the dirty child iterator for its parent (a).
*/
htree.writeNodeRecursive(f);
assertFalse(f.isDirty());
assertTrue(f.isPersistent());
// direct children of f's parent (a).
assertSameIterator(new AbstractPage[] { d, e, f },
a.childIterator(false/* onlyDirty */));
assertSameIterator(new AbstractPage[] { e },
a.childIterator(true/* onlyDirty */));
// verify the post-order iterator.
assertSameIterator(new AbstractPage[] { g, h, i, d, e, f, a, b,
c, root },
htree.root.postOrderNodeIterator(false/* dirtyOnly */));
assertSameIterator(new AbstractPage[] { e, a, root },
htree.root.postOrderNodeIterator(true/* dirtyOnly */));
/*
* write (a) onto the store and verify that it is no longer
* visited by the dirty child iterator for its parent.
*/
htree.writeNodeRecursive(a);
assertFalse(a.isDirty());
assertTrue(a.isPersistent());
// direct children of a's parent (root).
assertSameIterator(new AbstractPage[] { a, b, c },
htree.root.childIterator(false/* onlyDirty */));
assertSameIterator(new AbstractPage[] {},
htree.root.childIterator(true/* onlyDirty */));
// verify the post-order iterator.
assertSameIterator(new AbstractPage[] { g, h, i, d, e, f, a, b,
c, root },
htree.root.postOrderNodeIterator(false/* dirtyOnly */));
assertSameIterator(new AbstractPage[] { root },
htree.root.postOrderNodeIterator(true/* dirtyOnly */));
// Verify dirty/clear state before insert of duplicate key.
assertFalse(d.isDirty());
assertTrue(d.isPersistent());
assertFalse(g.isDirty());
assertTrue(g.isPersistent());
}
/**
* insert a duplicate key (3). this goes into (g) which becomes full
* but is not split. verify that the children of the parent (d) are
* unchanged but that (g) is no longer persistent and is now dirty.
* Since the parent (d) of (g) was persistent, copy-on-write was
* also invoked for (d) and it is now mutable and dirty.
*
* Note: Some of the page references will have been changed by
* copy-on-write, so we re-validate the index structure at this
* time. The expected post-condition is:
*
* <pre>
* D#95* [2] (D#05*,B#58 ,B#62 ,B#62 )
* D#05* [2] (D#99*,B#65 ,B#94 ,B#94 )
* D#99* [2] (B#48*,B#99 ,B#35 ,B#35 )
* B#48* [2] ([1](00000001),[2](00000010),[3](00000011),[3](00000011))
* B#99 [2] ([4](00000100),[5](00000101),-,-)
* B#35 [1] (-,-,-,-)
* B#65 [2] (-,-,-,-)
* B#94 [1] (-,-,-,-)
* B#58 [2] (-,-,-,-)
* B#62 [1] (-,-,-,-)
* </pre>
*/
{
if (log.isInfoEnabled())
log.info("Before insert of duplicate key (3) into (g): "
+ htree.PP());
htree.insert(k3, v3);
if (log.isInfoEnabled())
log.info("After insert of duplicate key (3) into (g): "
+ htree.PP());
// re-validate the index structure.
final DirectoryPage root = htree.root;
final DirectoryPage a = (DirectoryPage) htree.root.getChild(0);
final BucketPage b = (BucketPage) htree.root.getChild(1);
final BucketPage c = (BucketPage) htree.root.getChild(2);
assertTrue(c == (BucketPage) htree.root.getChild(3));
final DirectoryPage d = (DirectoryPage) a.getChild(0);
final BucketPage e = (BucketPage) a.getChild(1);
final BucketPage f = (BucketPage) a.getChild(2);
assertTrue(f == (BucketPage) a.getChild(3));
final BucketPage g = (BucketPage) d.getChild(0);
final BucketPage h = (BucketPage) d.getChild(1);
final BucketPage i = (BucketPage) d.getChild(2);
assertTrue(i == (BucketPage) d.getChild(3));
assertTrue(d.isDirty());
assertFalse(d.isPersistent());
assertTrue(g.isDirty());
assertFalse(g.isPersistent());
// check dirty iterator for (g)'s parent (d).
assertSameIterator(new AbstractPage[] { g, h, i },
d.childIterator(false/* onlyDirty */));
assertSameIterator(new AbstractPage[] { g },
d.childIterator(true/* onlyDirty */));
// check dirty iterator for (d)'s parent (a).
assertSameIterator(new AbstractPage[] { d, e, f },
a.childIterator(false/* onlyDirty */));
assertSameIterator(new AbstractPage[] { d },
a.childIterator(true/* onlyDirty */));
// check dirty iterator for (a)'s parent (root)
assertSameIterator(new AbstractPage[] { a, b, c },
htree.root.childIterator(false/* onlyDirty */));
assertSameIterator(new AbstractPage[] { a },
htree.root.childIterator(true/* onlyDirty */));
// verify the post-order iterator.
assertSameIterator(new AbstractPage[] { g, h, i, d, e, f, a, b,
c, root },
htree.root.postOrderNodeIterator(false/* dirtyOnly */));
assertSameIterator(new AbstractPage[] { g, d, a, root },
htree.root.postOrderNodeIterator(true/* dirtyOnly */));
/*
* Write the root node of the tree onto the store.
*/
htree.writeNodeRecursive(htree.root);
assertFalse(htree.root.isDirty());
assertFalse(a.isDirty());
assertFalse(d.isDirty());
// check dirty iterator for (root)
assertSameIterator(new AbstractPage[] { a, b, c },
htree.root.childIterator(false/* onlyDirty */));
assertSameIterator(new AbstractPage[] {},
htree.root.childIterator(true/* onlyDirty */));
// check dirty iterator for (a).
assertSameIterator(new AbstractPage[] { d, e, f },
a.childIterator(false/* onlyDirty */));
assertSameIterator(new AbstractPage[] {},
a.childIterator(true/* onlyDirty */));
// check dirty iterator for (d)
assertSameIterator(new AbstractPage[] { g, h, i },
d.childIterator(false/* onlyDirty */));
assertSameIterator(new AbstractPage[] {},
d.childIterator(true/* onlyDirty */));
// verify the post-order iterator.
assertSameIterator(new AbstractPage[] { g, h, i, d, e, f, a, b,
c, root },
htree.root.postOrderNodeIterator(false/* dirtyOnly */));
assertSameIterator(new AbstractPage[] { },
htree.root.postOrderNodeIterator(true/* dirtyOnly */));
}
} finally {
store.destroy();
}
}
// /**
// * Test ability to visit the dirty nodes of the tree in a post-order
// * traversal. This version of the test verifies that the dirty post-order
// * iterator will visit the same nodes as the normal post-order iterator
// * since all nodes are dirty.
// */
// public void test_dirtyPostOrderIterator01() {
//
// BTree btree = getBTree(3);
//
// final Leaf a = (Leaf) btree.root;
//
// SimpleEntry v1 = new SimpleEntry(1);
// SimpleEntry v2 = new SimpleEntry(2);
// SimpleEntry v3 = new SimpleEntry(3);
// SimpleEntry v4 = new SimpleEntry(4);
// SimpleEntry v6 = new SimpleEntry(6);
// SimpleEntry v5 = new SimpleEntry(5);
// SimpleEntry v7 = new SimpleEntry(7);
// SimpleEntry v9 = new SimpleEntry(9);
//
// // empty tree visits the root leaf.
// assertSameIterator(new IAbstractNode[] { btree.root }, btree.root
// .postOrderNodeIterator(false));
// assertSameIterator(new IAbstractNode[] { btree.root }, btree.root
// .postOrderNodeIterator(true));
//
// // fill up the root leaf.
// btree.insert(TestKeyBuilder.asSortKey(3), v3);
// btree.insert(TestKeyBuilder.asSortKey(5), v5);
// btree.insert(TestKeyBuilder.asSortKey(7), v7);
//
// // split the root leaf.
// btree.insert(TestKeyBuilder.asSortKey(9), v9);
// final Node c = (Node) btree.root;
// assertKeys(new int[]{7},c);
// assertEquals(a,c.getChild(0));
// final Leaf b = (Leaf)c.getChild(1);
// assertKeys(new int[]{3,5},a);
// assertValues(new Object[]{v3,v5}, a);
// assertKeys(new int[]{7,9},b);
// assertValues(new Object[]{v7,v9}, b);
//
// // verify iterator.
// assertSameIterator(new IAbstractNode[] { a, b, c }, btree.root
// .postOrderNodeIterator(false));
// assertSameIterator(new IAbstractNode[] { a, b, c }, btree.root
// .postOrderNodeIterator(true));
//
// /*
// * split another leaf so that there are now three children to visit. at
// * this point the root is full.
// */
// btree.insert(TestKeyBuilder.asSortKey(1), v1);
// btree.insert(TestKeyBuilder.asSortKey(2), v2);
// assertKeys(new int[]{3,7},c);
// assertEquals(a,c.getChild(0));
// Leaf d = (Leaf)c.getChild(1);
// assertEquals(b,c.getChild(2));
// assertKeys(new int[]{1,2},a);
// assertValues(new Object[]{v1,v2}, a);
// assertKeys(new int[]{3,5},d);
// assertValues(new Object[]{v3,v5}, d);
// assertKeys(new int[]{7,9},b);
// assertValues(new Object[]{v7,v9}, b);
//
// // verify iterator
// assertSameIterator(new IAbstractNode[] { a, d, b, c }, btree.root
// .postOrderNodeIterator(false));
// assertSameIterator(new IAbstractNode[] { a, d, b, c }, btree.root
// .postOrderNodeIterator(true));
//
// /*
// * cause another leaf (d) to split, forcing the split to propagate to and
// * split the root and the tree to increase in height.
// */
// btree.insert(TestKeyBuilder.asSortKey(4), v4);
// btree.insert(TestKeyBuilder.asSortKey(6), v6);
//// btree.dump(Level.DEBUG,System.err);
// assertNotSame(c,btree.root);
// final Node g = (Node)btree.root;
// assertKeys(new int[]{5},g);
// assertEquals(c,g.getChild(0));
// final Node f = (Node)g.getChild(1);
// assertKeys(new int[]{3},c);
// assertEquals(a,c.getChild(0));
// assertEquals(d,c.getChild(1));
// assertKeys(new int[]{1,2},a);
// assertValues(new Object[]{v1,v2}, a);
// assertKeys(new int[]{3,4},d);
// assertValues(new Object[]{v3,v4}, d);
// assertKeys(new int[]{7},f);
// Leaf e = (Leaf)f.getChild(0);
// assertEquals(b,f.getChild(1));
// assertKeys(new int[]{5,6},e);
// assertValues(new Object[]{v5,v6}, e);
// assertKeys(new int[]{7,9},b);
// assertValues(new Object[]{v7,v9}, b);
//
// // verify iterator
// assertSameIterator(new IAbstractNode[] { a, d, c, e, b, f, g }, btree.root
// .postOrderNodeIterator(false));
// assertSameIterator(new IAbstractNode[] { a, d, c, e, b, f, g }, btree.root
// .postOrderNodeIterator(true));
//
// /*
// * remove a key (4) from (d) forcing (d,a) to merge into (d) and (a) to
// * be deleted. this causes (c,f) to merge as well, which in turn forces
// * the root to be replaced by (c).
// */
// assertEquals(v4,btree.remove(TestKeyBuilder.asSortKey(4)));
//// btree.dump(Level.DEBUG,System.err);
// assertKeys(new int[]{5,7},c);
// assertEquals(d,c.getChild(0));
// assertEquals(e,c.getChild(1));
// assertEquals(b,c.getChild(2));
// assertKeys(new int[]{1,2,3},d);
// assertValues(new Object[]{v1,v2,v3}, d);
// assertKeys(new int[]{5,6},e);
// assertValues(new Object[]{v5,v6}, e);
// assertKeys(new int[]{7,9},b);
// assertValues(new Object[]{v7,v9}, b);
// assertTrue(a.isDeleted());
//
// // verify iterator
// assertSameIterator(new IAbstractNode[] { d, e, b, c }, btree.root
// .postOrderNodeIterator(false));
// assertSameIterator(new IAbstractNode[] { d, e, b, c }, btree.root
// .postOrderNodeIterator(true));
//
// /*
// * remove a key (7) from a leaf (b) forcing two leaves to join and
// * verify the visitation order.
// */
// assertEquals(v7,btree.remove(TestKeyBuilder.asSortKey(7)));
// btree.dump(Level.DEBUG,System.err);
// assertKeys(new int[]{5},c);
// assertEquals(d,c.getChild(0));
// assertEquals(b,c.getChild(1));
// assertKeys(new int[]{1,2,3},d);
// assertValues(new Object[]{v1,v2,v3}, d);
// assertKeys(new int[]{5,6,9},b);
// assertValues(new Object[]{v5,v6,v9}, b);
// assertTrue(e.isDeleted());
//
// // verify iterator
// assertSameIterator(new IAbstractNode[] { d, b, c }, btree.root
// .postOrderNodeIterator(false));
// assertSameIterator(new IAbstractNode[] { d, b, c }, btree.root
// .postOrderNodeIterator(true));
//
// /*
// * remove keys from a leaf forcing the remaining two leaves to join and
// * verify the visitation order.
// */
// assertEquals(v3,btree.remove(TestKeyBuilder.asSortKey(3)));
// assertEquals(v5,btree.remove(TestKeyBuilder.asSortKey(5)));
// assertEquals(v6,btree.remove(TestKeyBuilder.asSortKey(6)));
// assertKeys(new int[]{1,2,9},b);
// assertValues(new Object[]{v1,v2,v9}, b);
// assertTrue(d.isDeleted());
// assertTrue(c.isDeleted());
//
// // verify iterator
// assertSameIterator(new IAbstractNode[] { b }, btree.root
// .postOrderNodeIterator(false));
// assertSameIterator(new IAbstractNode[] { b }, btree.root
// .postOrderNodeIterator(true));
//
// }
//
// /**
// * Test ability to visit the dirty nodes of the tree in a post-order
// * traversal. This version of the test writes out some nodes and/or leaves
// * in order to verify that the post-order iterator will visit only those
// * nodes and leaves that are currently dirty. Note that writing out a node
// * or leaf makes it immutable. In order to make the node or leaf dirty again
// * we have to modify it, which triggers copy-on-write. Copy on write
// * propagates up from the leaf where we make the mutation and causes any
// * immutable parents to be cloned as well. Nodes and leaves that have been
// * cloned by copy-on-write are distinct objects from their immutable
// * predecessors.
// */
// public void test_dirtyPostOrderIterator02() {
//
// BTree btree = getBTree(3);
//
// Leaf a = (Leaf) btree.root;
//
// SimpleEntry v1 = new SimpleEntry(1);
// SimpleEntry v2 = new SimpleEntry(2);
// SimpleEntry v3 = new SimpleEntry(3);
// SimpleEntry v4 = new SimpleEntry(4);
// SimpleEntry v6 = new SimpleEntry(6);
// SimpleEntry v5 = new SimpleEntry(5);
// SimpleEntry v7 = new SimpleEntry(7);
// SimpleEntry v9 = new SimpleEntry(9);
//
// // empty tree visits the root leaf.
// assertSameIterator(new IAbstractNode[] { btree.root }, btree.root
// .postOrderNodeIterator(false));
// assertSameIterator(new IAbstractNode[] { btree.root }, btree.root
// .postOrderNodeIterator(true));
// /*
// * write out the root leaf on the store and verify that the dirty
// * iterator does not visit anything while the normal iterator visits the
// * root.
// */
// btree.writeNodeRecursive(btree.root);
// assertSameIterator(new IAbstractNode[] { btree.root }, btree.root
// .postOrderNodeIterator(false));
// assertSameIterator(new IAbstractNode[] {}, btree.root
// .postOrderNodeIterator(true));
//
// /*
// * Fill up the root leaf. Since it was immutable, this will trigger
// * copy-on-write. We verify that the root leaf reference is changed
// * and verify that both iterators now visit the root.
// */
// assertEquals(a,btree.root);
// btree.insert(TestKeyBuilder.asSortKey(3), v3);
// assertNotSame(a,btree.root);
// a = (Leaf)btree.root; // new reference for the root leaf.
// assertSameIterator(new IAbstractNode[] { btree.root }, btree.root
// .postOrderNodeIterator(false));
// assertSameIterator(new IAbstractNode[] { btree.root }, btree.root
// .postOrderNodeIterator(true));
// btree.insert(TestKeyBuilder.asSortKey(5), v5);
// btree.insert(TestKeyBuilder.asSortKey(7), v7);
//
// // split the root leaf.
// btree.insert(TestKeyBuilder.asSortKey(9), v9);
// Node c = (Node) btree.root;
// assertKeys(new int[]{7},c);
// assertEquals(a,c.getChild(0));
// Leaf b = (Leaf)c.getChild(1);
// assertKeys(new int[]{3,5},a);
// assertValues(new Object[]{v3,v5}, a);
// assertKeys(new int[]{7,9},b);
// assertValues(new Object[]{v7,v9}, b);
//
// // verify iterator.
// assertSameIterator(new IAbstractNode[] { a, b, c }, btree.root
// .postOrderNodeIterator(false));
// assertSameIterator(new IAbstractNode[] { a, b, c }, btree.root
// .postOrderNodeIterator(true));
//
// /*
// * write out (a) and verify the iterator behaviors.
// */
// btree.writeNodeOrLeaf(a);
// assertTrue(a.isPersistent());
// assertFalse(b.isPersistent());
// assertFalse(c.isPersistent());
// assertSameIterator(new IAbstractNode[] { a, b, c }, btree.root
// .postOrderNodeIterator(false));
// assertSameIterator(new IAbstractNode[] { b, c }, btree.root
// .postOrderNodeIterator(true));
//
// /*
// * write out (c) and verify the iterator behaviors.
// */
// btree.writeNodeRecursive(c);
// assertTrue(a.isPersistent());
// assertTrue(b.isPersistent());
// assertTrue(c.isPersistent());
// assertSameIterator(new IAbstractNode[] { a, b, c }, btree.root
// .postOrderNodeIterator(false));
// assertSameIterator(new IAbstractNode[] { }, btree.root
// .postOrderNodeIterator(true));
//
// /*
// * split another leaf (a) so that there are now three children to visit.
// * at this point the root is full.
// */
// assertTrue(a.isPersistent());
// assertTrue(b.isPersistent());
// assertTrue(c.isPersistent());
// btree.insert(TestKeyBuilder.asSortKey(1), v1); // triggers copy on write for (a) and (c).
// assertNotSame(c,btree.root);
// c = (Node)btree.root;
// assertNotSame(a,c.getChild(0));
// a = (Leaf)c.getChild(0);
// assertEquals(b,c.getChild(1)); // b was not copied.
// assertFalse(a.isPersistent());
// assertTrue(b.isPersistent());
// assertFalse(c.isPersistent());
// btree.insert(TestKeyBuilder.asSortKey(2), v2);
// assertKeys(new int[]{3,7},c);
// assertEquals(a,c.getChild(0));
// Leaf d = (Leaf)c.getChild(1);
// assertEquals(b,c.getChild(2));
// assertKeys(new int[]{1,2},a);
// assertValues(new Object[]{v1,v2}, a);
// assertKeys(new int[]{3,5},d);
// assertValues(new Object[]{v3,v5}, d);
// assertKeys(new int[]{7,9},b);
// assertValues(new Object[]{v7,v9}, b);
//
// // verify iterator
// assertFalse(a.isPersistent());
// assertTrue(b.isPersistent());
// assertFalse(c.isPersistent());
// assertSameIterator(new IAbstractNode[] { a, d, b, c }, btree.root
// .postOrderNodeIterator(false));
// assertSameIterator(new IAbstractNode[] { a, d, c }, btree.root
// .postOrderNodeIterator(true));
//
// /*
// * cause another leaf (d) to split, forcing the split to propagate to and
// * split the root and the tree to increase in height.
// */
// btree.insert(TestKeyBuilder.asSortKey(4), v4);
// btree.insert(TestKeyBuilder.asSortKey(6), v6);
//// btree.dump(Level.DEBUG,System.err);
// assertNotSame(c,btree.root);
// final Node g = (Node)btree.root;
// assertKeys(new int[]{5},g);
// assertEquals(c,g.getChild(0));
// final Node f = (Node)g.getChild(1);
// assertKeys(new int[]{3},c);
// assertEquals(a,c.getChild(0));
// assertEquals(d,c.getChild(1));
// assertKeys(new int[]{1,2},a);
// assertValues(new Object[]{v1,v2}, a);
// assertKeys(new int[]{3,4},d);
// assertValues(new Object[]{v3,v4}, d);
// assertKeys(new int[]{7},f);
// Leaf e = (Leaf)f.getChild(0);
// assertEquals(b,f.getChild(1));
// assertKeys(new int[]{5,6},e);
// assertValues(new Object[]{v5,v6}, e);
// assertKeys(new int[]{7,9},b);
// assertValues(new Object[]{v7,v9}, b);
//
// // verify iterator
// assertSameIterator(new IAbstractNode[] { a, d, c, e, b, f, g }, btree.root
// .postOrderNodeIterator(false));
// assertSameIterator(new IAbstractNode[] { a, d, c, e, f, g }, btree.root
// .postOrderNodeIterator(true));
//
// /*
// * write out a subtree and revalidate the iterators.
// */
// btree.writeNodeRecursive(c);
// assertSameIterator(new IAbstractNode[] { a, d, c, e, b, f, g }, btree.root
// .postOrderNodeIterator(false));
// assertSameIterator(new IAbstractNode[] { e, f, g }, btree.root
// .postOrderNodeIterator(true));
//
// /*
// * write out a leaf and revalidate the iterators.
// */
// btree.writeNodeRecursive(e);
// assertSameIterator(new IAbstractNode[] { a, d, c, e, b, f, g }, btree.root
// .postOrderNodeIterator(false));
// assertSameIterator(new IAbstractNode[] { f, g }, btree.root
// .postOrderNodeIterator(true));
//
// /*
// * write out the entire tree and revalidate the iterators.
// */
// btree.writeNodeRecursive(g);
// assertSameIterator(new IAbstractNode[] { a, d, c, e, b, f, g }, btree.root
// .postOrderNodeIterator(false));
// assertSameIterator(new IAbstractNode[] {}, btree.root
// .postOrderNodeIterator(true));
//
// /*
// * remove a key (4) from (d) forcing (d,a) to merge into (d) and (a) to
// * be deleted. this causes (c,f) to merge as well, which in turn forces
// * the root to be replaced by (c).
// *
// * the following are cloned: d, c, g.
// */
// assertEquals(v4,btree.remove(TestKeyBuilder.asSortKey(4)));
// assertNotSame(g,btree.root);
// assertNotSame(c,btree.root);
// c = (Node) btree.root;
// assertNotSame(d,c.getChild(0));
// d = (Leaf) c.getChild(0);
//// btree.dump(Level.DEBUG,System.err);
// assertKeys(new int[]{5,7},c);
// assertEquals(d,c.getChild(0));
// assertEquals(e,c.getChild(1));
// assertEquals(b,c.getChild(2));
// assertKeys(new int[]{1,2,3},d);
// assertValues(new Object[]{v1,v2,v3}, d);
// assertKeys(new int[]{5,6},e);
// assertValues(new Object[]{v5,v6}, e);
// assertKeys(new int[]{7,9},b);
// assertValues(new Object[]{v7,v9}, b);
// assertTrue(a.isDeleted());
// assertTrue(f.isDeleted());
//
// // verify iterator
// assertSameIterator(new IAbstractNode[] { d, e, b, c }, btree.root
// .postOrderNodeIterator(false));
// assertSameIterator(new IAbstractNode[] { d, c }, btree.root
// .postOrderNodeIterator(true));
//
// /*
// * remove a key (7) from a leaf (b) forcing two leaves (b,e) into (b) to
// * join and verify the visitation order.
// */
// assertEquals(v7,btree.remove(TestKeyBuilder.asSortKey(7)));
// btree.dump(Level.DEBUG,System.err);
// assertKeys(new int[]{5},c);
// assertEquals(d,c.getChild(0));
// assertNotSame(b,c.getChild(1));
// b = (Leaf) c.getChild(1);
// assertKeys(new int[]{1,2,3},d);
// assertValues(new Object[]{v1,v2,v3}, d);
// assertKeys(new int[]{5,6,9},b);
// assertValues(new Object[]{v5,v6,v9}, b);
// assertTrue(e.isDeleted());
//
// // verify iterator
// assertSameIterator(new IAbstractNode[] { d, b, c }, btree.root
// .postOrderNodeIterator(false));
// assertSameIterator(new IAbstractNode[] { d, b, c }, btree.root
// .postOrderNodeIterator(true));
// /*
// * write out the root and verify the visitation orders.
// */
// btree.writeNodeRecursive(c);
// assertSameIterator(new IAbstractNode[] { d, b, c }, btree.root
// .postOrderNodeIterator(false));
// assertSameIterator(new IAbstractNode[] {}, btree.root
// .postOrderNodeIterator(true));
//
// /*
// * remove keys from a leaf (b) forcing the remaining two leaves (b,d) to
// * join into (b). Since there is only one leaf, that leaf now becomes
// * the new root leaf of the tree.
// */
// assertEquals(c,btree.root);
// assertEquals(d,c.getChild(0));
// assertEquals(b,c.getChild(1));
// assertEquals(v3, btree.remove(TestKeyBuilder.asSortKey(3))); // remove from (d)
// assertNotSame(c,btree.root); // c was cloned.
// c = (Node) btree.root;
// assertNotSame(d,c.getChild(0));
// d = (Leaf)c.getChild(0); // d was cloned.
// assertEquals(b,c.getChild(1));
// assertEquals(v5,btree.remove(TestKeyBuilder.asSortKey(5))); // remove from (b)
// assertNotSame(b,c.getChild(1));
// b = (Leaf)c.getChild(1); // b was cloned.
// assertEquals(v6,btree.remove(TestKeyBuilder.asSortKey(6))); // remove from (b)
// assertKeys(new int[]{1,2,9},b);
// assertValues(new Object[]{v1,v2,v9}, b);
// assertTrue(d.isDeleted());
// assertTrue(c.isDeleted());
//
// // verify iterator
// assertSameIterator(new IAbstractNode[] { b }, btree.root
// .postOrderNodeIterator(false));
// assertSameIterator(new IAbstractNode[] { b }, btree.root
// .postOrderNodeIterator(true));
//
// /*
// * write out the root and reverify the iterators.
// */
// btree.writeNodeRecursive(b);
// assertSameIterator(new IAbstractNode[] { b }, btree.root
// .postOrderNodeIterator(false));
// assertSameIterator(new IAbstractNode[] {}, btree.root
// .postOrderNodeIterator(true));
//
// }
}