/**
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.btree;
import org.apache.log4j.Level;
import com.bigdata.btree.keys.TestKeyBuilder;
/**
* Test suite for iterators that visit only dirty nodes or leaves. This test
* suite was factored apart from {@link TestIterators} since this suite relies
* on (and to some extent validates) both node and leaf IO and copy-on-write
* mechanisms.
*
* @see Node#childIterator(boolean)
* @see AbstractNode#postOrderNodeIterator(boolean, boolean)
*
* @see TestIterators, which handles iterators that do not differentiate between
* clear and dirty nodes.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*/
public class TestDirtyIterators extends AbstractBTreeTestCase {
/**
*
*/
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 - see {@link #getBTree(int)}, which
* throws an exception if the tree attempts a node eviction.
*/
public void test_dirtyChildIterator01() {
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 v5 = new SimpleEntry(5);
SimpleEntry v7 = new SimpleEntry(7);
SimpleEntry v9 = new SimpleEntry(9);
// 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);
assertTrue(a.isDirty());
assertTrue(b.isDirty());
// verify visiting all children.
assertSameIterator(new IAbstractNode[] { a, b }, ((Node) btree.root)
.childIterator(false));
assertSameIterator(new IAbstractNode[] { a, b }, ((Node) btree.root)
.childIterator(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);
assertTrue(a.isDirty());
assertTrue(d.isDirty());
assertTrue(b.isDirty());
// verify visiting all children.
assertSameIterator(new IAbstractNode[] { a, d, b }, ((Node) btree.root)
.childIterator(false));
assertSameIterator(new IAbstractNode[] { a, d, b }, ((Node) btree.root)
.childIterator(true));
/*
* remove a key from a leaf forcing two leaves to join and verify the
* visitation order.
*/
assertEquals(v1,btree.remove(TestKeyBuilder.asSortKey(1)));
assertKeys(new int[]{7},c);
assertEquals(a,c.getChild(0));
assertEquals(b,c.getChild(1));
assertKeys(new int[]{2,3,5},a);
assertValues(new Object[]{v2,v3,v5}, a);
assertKeys(new int[]{7,9},b);
assertValues(new Object[]{v7,v9}, b);
assertTrue(d.isDeleted());
assertTrue(a.isDirty());
assertTrue(b.isDirty());
// verify visiting all children.
assertSameIterator(new IAbstractNode[] { a, b }, ((Node) btree.root)
.childIterator(false));
assertSameIterator(new IAbstractNode[] { a, b }, ((Node) btree.root)
.childIterator(true));
/*
* Note: the test ends here since there must be either 2 or 3 children
* for the root node. If we force the remaining leaves to join, then
* the root node will be replaced by a root leaf.
*/
}
/**
* 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() {
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 v5 = new SimpleEntry(5);
SimpleEntry v7 = new SimpleEntry(7);
SimpleEntry v8 = new SimpleEntry(8);
SimpleEntry v9 = new SimpleEntry(9);
// 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);
assertTrue(a.isDirty());
assertTrue(b.isDirty());
// verify visiting all children.
assertSameIterator(new IAbstractNode[] { a, b }, ((Node) btree.root)
.childIterator(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);
assertTrue(a.isDirty());
assertTrue(d.isDirty());
assertTrue(b.isDirty());
// verify visiting all children.
assertSameIterator(new IAbstractNode[] { a, d, b }, ((Node) btree.root)
.childIterator(true));
// write (a) onto the store and verify that it is no longer visited.
btree.writeNodeOrLeaf(a);
assertFalse(a.isDirty());
assertTrue(a.isPersistent());
assertSameIterator(new IAbstractNode[] { d, b }, ((Node) btree.root)
.childIterator(true));
// write (b) onto the store and verify that it is no longer visited.
btree.writeNodeOrLeaf(b);
assertFalse(b.isDirty());
assertTrue(b.isPersistent());
assertSameIterator(new IAbstractNode[] { d }, ((Node) btree.root)
.childIterator(true));
// write (d) onto the store and verify that it is no longer visited.
btree.writeNodeOrLeaf(d);
assertFalse(d.isDirty());
assertTrue(d.isPersistent());
assertSameIterator(new IAbstractNode[] {}, ((Node) btree.root)
.childIterator(true));
/*
* remove a key from a leaf forcing two leaves to join and verify the
* visitation order. this triggers copy-on-write for (a) and (a) is
* dirty as a post-condition.
*/
assertEquals(v1,btree.remove(TestKeyBuilder.asSortKey(1)));
assertKeys(new int[]{7},c);
assertNotSame(a,c.getChild(0));
Leaf a1 = (Leaf)c.getChild(0);
assertEquals(b,c.getChild(1));
assertKeys(new int[]{2,3,5},a1);
assertValues(new Object[]{v2,v3,v5}, a1);
assertKeys(new int[]{7,9},b);
assertValues(new Object[]{v7,v9}, b);
assertTrue(d.isDeleted());
assertTrue(a1.isDirty());
assertFalse(b.isDirty());
// verify visiting dirty children.
assertSameIterator(new IAbstractNode[] { a1 }, ((Node) btree.root)
.childIterator(true));
/*
* insert a key that will go into (b). since (b) is immutable this
* triggers copy-on-write.
*/
btree.insert(TestKeyBuilder.asSortKey(8),v8);
assertKeys(new int[]{7},c);
assertEquals(a1,c.getChild(0));
assertNotSame(b,c.getChild(1));
Leaf b1 = (Leaf)c.getChild(1);
assertKeys(new int[]{2,3,5},a1);
assertValues(new Object[]{v2,v3,v5}, a1);
assertKeys(new int[]{7,8,9},b1);
assertValues(new Object[]{v7,v8,v9}, b1);
assertTrue(c.isDirty());
assertTrue(d.isDeleted());
assertTrue(a1.isDirty());
assertTrue(b1.isDirty());
// verify visiting dirty children.
assertSameIterator(new IAbstractNode[] { a1, b1 }, ((Node) btree.root)
.childIterator(true));
/*
* write the root node of the tree onto the store.
*/
btree.writeNodeRecursive(c);
assertFalse(c.isDirty());
assertFalse(a1.isDirty());
assertFalse(b1.isDirty());
// verify visiting dirty children.
assertSameIterator(new IAbstractNode[] {}, ((Node) btree.root)
.childIterator(true));
/*
* remove a key from (a1). since (a1) is immutable this triggers
* copy-on-write. since the root is immtuable, it is also copied.
*/
assertEquals(v2,btree.remove(TestKeyBuilder.asSortKey(2)));
assertNotSame(c,btree.root);
Node c1 = (Node)btree.root;
assertKeys(new int[]{7},c1);
assertNotSame(a1,c1.getChild(0));
Leaf a2 = (Leaf) c1.getChild(0);
assertEquals( b1, c1.getChild(1));
assertKeys(new int[]{3,5},a2);
assertValues(new Object[]{v3,v5}, a2);
assertKeys(new int[]{7,8,9},b1);
assertValues(new Object[]{v7,v8,v9}, b1);
assertTrue(c1.isDirty());
assertTrue(a2.isDirty());
assertFalse(b1.isDirty());
// verify visiting dirty children.
assertSameIterator(new IAbstractNode[] {a2}, ((Node) btree.root)
.childIterator(true));
/*
* Note: the test ends here since there must be either 2 or 3 children
* for the root node. If we force the remaining leaves to join, then
* the root node will be replaced by a root leaf.
*/
}
/**
* 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));
}
}