/*
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 May 20, 2008
*/
package com.bigdata.btree;
import java.util.Iterator;
import org.apache.log4j.Level;
import com.bigdata.btree.IndexSegment.ImmutableLeafCursor;
import com.bigdata.btree.IndexSegment.ImmutableNodeFactory.ImmutableLeaf;
import com.bigdata.io.DirectBufferPool;
/**
* Adds some methods for testing an {@link IndexSegment} for consistency.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*/
public class AbstractIndexSegmentTestCase extends AbstractBTreeTestCase {
/**
*
*/
public AbstractIndexSegmentTestCase() {
}
/**
* @param name
*/
public AbstractIndexSegmentTestCase(String name) {
super(name);
}
/**
* apply dump() as a structural validation of the tree. note that we
* have to materialize the leaves in the generated index segment since
* dump does not materialize a child from its Addr if it is not already
* resident.
*/
protected void dumpIndexSegment(final IndexSegment seg) {
if (log.isInfoEnabled())
log.info("file=" + seg.getStore().getFile());
// materialize the leaves.
{
int n = 0, m = 0;
final ILeafCursor<?> cursor = seg.newLeafCursor(SeekEnum.First);
while (cursor.next() != null) {
n++;
}
cursor.last();
while (cursor.prior() != null) {
m++;
}
/*
* Note: n == m iff the same number of leaves were visited in each
* direction.
*/
assertEquals(n, m);
}
/*
* Dump the tree to validate it.
*
* Note: This will only dump the root node since the [cursor] for an
* IndexSegment does not materialize the intermediate nodes and dump()
* only dumps those nodes and leaves which are already materialized.
*/
{
log.info("dumping root");
assertTrue(seg.getRoot().toShortString(), seg.dump(Level.DEBUG,
System.err));
}
/*
* Dump the root, nodes, and leaves using the post-order iterator.
*/
{
log.info("dumping nodes");
final Iterator<AbstractNode> itr = seg.getRoot()
.postOrderNodeIterator();
while (itr.hasNext()) {
final AbstractNode node = itr.next();
assertTrue(node.toShortString(), node.dump(Level.DEBUG,
System.err));
}
}
/*
* Dump the leaves in forward order using the tuple cursor.
*/
{
log.info("dumping leaves (forward tuple cursor)");
final ILeafCursor<?> cursor = seg.newLeafCursor(SeekEnum.First);
Leaf leaf = cursor.leaf();
assertTrue(leaf.toShortString(), leaf.dump(Level.DEBUG, System.err));
while ((leaf = cursor.next()) != null) {
assertTrue(leaf.toShortString(), leaf.dump(Level.DEBUG,
System.err));
}
}
/*
* Dump the leaves in reverse order using the tuple cursor.
*/
{
log.info("dumping leaves (reverse tuple cursor)");
final ILeafCursor<?> cursor = seg.newLeafCursor(SeekEnum.Last);
Leaf leaf = cursor.leaf();
assertTrue(leaf.toShortString(), leaf.dump(Level.DEBUG, System.err));
while ((leaf = cursor.prior()) != null) {
assertTrue(leaf.toShortString(), leaf.dump(Level.DEBUG,
System.err));
}
}
}
/**
* Test forward leaf scan.
*/
static public void testForwardScan(final IndexSegment seg)
{
final int nleaves = seg.getStore().getCheckpoint().nleaves;
final ImmutableLeaf firstLeaf = seg.readLeaf(seg.getStore().getCheckpoint().addrFirstLeaf);
assertEquals("priorAddr", 0L, firstLeaf.getPriorAddr());
final ImmutableLeaf lastLeaf = seg.readLeaf(seg.getStore().getCheckpoint().addrLastLeaf);
assertEquals("nextAddr", 0L, lastLeaf.getNextAddr());
final ImmutableLeafCursor itr = seg.newLeafCursor(SeekEnum.First);
// if(LRUNexus.INSTANCE!=null)
// assertTrue(firstLeaf.getDelegate()==itr.leaf().getDelegate()); // Note: test depends on cache!
ImmutableLeaf priorLeaf = itr.leaf();
int n = 1;
for (int i = 1; i < seg.getLeafCount(); i++) {
final ImmutableLeaf current = itr.next();
assertEquals("priorAddr", priorLeaf.getIdentity(), current
.getPriorAddr());
priorLeaf = current;
if (current == lastLeaf) {
// last leaf.
assertNull(itr.next());
}
n++;
}
assertEquals("#leaves",nleaves,n);
}
/**
* Test reverse leaf scan.
*
* Note: the scan starts with the last leaf in the key order and then
* proceeds in reverse key order.
*/
static public void testReverseScan(final IndexSegment seg) {
final int nleaves = seg.getStore().getCheckpoint().nleaves;
final ImmutableLeaf firstLeaf = seg.readLeaf(seg.getStore()
.getCheckpoint().addrFirstLeaf);
assertEquals("priorAddr", 0L, firstLeaf.getPriorAddr());
final ImmutableLeaf lastLeaf = seg.readLeaf(seg.getStore()
.getCheckpoint().addrLastLeaf);
assertEquals("nextAddr", 0L, lastLeaf.getNextAddr());
final ImmutableLeafCursor itr = seg.newLeafCursor(SeekEnum.Last);
// if(LRUNexus.INSTANCE!=null)
// assertTrue(lastLeaf.getDelegate() == itr.leaf().getDelegate()); // Note: test depends on cache!
ImmutableLeaf nextLeaf = itr.leaf();
int n = 1;
for (int i = 1; i < seg.getLeafCount(); i++) {
final ImmutableLeaf current = itr.prior();
assertEquals("nextAddr", nextLeaf.getIdentity(), current
.getNextAddr());
nextLeaf = current;
if (current == firstLeaf) {
// last leaf.
assertNull(itr.prior());
}
n++;
}
assertEquals("#leaves",nleaves,n);
}
/**
* Compares the {@link IndexSegmentMultiBlockIterator} against the standard
* {@link BTree} iterator.
*
* @param expected
* The ground truth {@link BTree}.
* @param actual
* The {@link IndexSegment}.
*/
static public void testMultiBlockIterator(final BTree expected,
final IndexSegment actual) {
final long actualTupleCount = doEntryIteratorTest(expected
.rangeIterator(), new IndexSegmentMultiBlockIterator(actual,
DirectBufferPool.INSTANCE, null/* fromKey */,
null/* toKey */, IRangeQuery.DEFAULT));
// verifies based on what amounts to an exact range count.
assertEquals("entryCount", expected.getEntryCount(), actualTupleCount);
}
}