/**
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 17, 2007
*/
package com.bigdata.btree;
import com.bigdata.btree.BTree.PartitionedCounter;
import com.bigdata.rdf.lexicon.TermIdEncoder;
/**
* Test suite for the {@link IIndex#getCounter()} interface.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*/
public class TestIndexCounter extends AbstractBTreeTestCase {
/**
*
*/
public TestIndexCounter() {
}
/**
* @param name
*/
public TestIndexCounter(String name) {
super(name);
}
/**
* Unit test for {@link BTree.Counter} to verify basic increment behavior.
*/
public void test_counter() {
final BTree btree = getBTree(3);
final ICounter counter = btree.getCounter();
// initial value is zero for an unpartitioned index
assertEquals(0, counter.get());
// get() does not have a side-effect on the counter.
assertEquals(0, counter.get());
// inc() increments the value and _then_ returns the counter.
assertEquals(1, counter.incrementAndGet());
assertEquals(1, counter.get());
assertEquals(2, counter.incrementAndGet());
}
/**
* Unit test for overflow conditions at the int32 and int64 boundary for
* {@link BTree.Counter}.
*/
public void test_counter_overflow() {
final BTree btree = getBTree(3);
final ICounter counter = btree.getCounter();
final long maxSignedInt = (long) Integer.MAX_VALUE;
final long minSignedInt = 0xFFFFFFFFL & (long) Integer.MIN_VALUE;
final long minusOneInt = 0xFFFFFFFFL & (long) -1;
final long int32Overflow = 1L << 32;// bit 33 is on.
/*
* First explore when the counter crosses from max signed int to min
* signed int.
*/
// Artificially set the counter to max signed int.
btree.counter.set(maxSignedInt);
// Verify current value.
assertEquals(maxSignedInt, counter.get());
// Increment. Should now be min signed int.
assertEquals(minSignedInt, counter.incrementAndGet());
// Increment. Should now be moving towards zero.
assertEquals(minSignedInt + 1L, counter.incrementAndGet());
// Increment. Should now be moving towards zero.
assertEquals(minSignedInt + 2L, counter.incrementAndGet());
/*
* Now explore when the counter approaches a value which can only be
* expressed in 33 bits (bigger than an unsigned int).
*/
btree.counter.set(minusOneInt - 1);
// Verify current value.
assertEquals(minusOneInt - 1, counter.get());
// Increment. Should now be a long whose lower word is minus one signed
// int.
assertEquals(minusOneInt, counter.incrementAndGet());
// Increment. Should now be a long whose lower word is ZERO and whose
// upper word as the low bit set.
assertEquals(int32Overflow, counter.incrementAndGet());
/*
* Now explore when the counter approaches -1L and 0L.
*/
btree.counter.set(-2L);
// Verify current value.
assertEquals(-2L, counter.get());
// Increment to -1L.
assertEquals(-1L, counter.incrementAndGet());
// Increment to 0L fails (counter overflow).
try {
counter.incrementAndGet();
fail("Expecting counter overflow");
} catch (RuntimeException ex) {
if (log.isInfoEnabled())
log.info("Ignoring expected exception: " + ex);
}
}
/**
* Unit tests for {@link PartitionedCounter} when the partition identifier
* is ZERO (0).
*/
public void test_partitionedCounter_pid0() {
final BTree btree = getBTree(3);
final int pid = 0;
final ICounter counter = new PartitionedCounter(pid, new BTree.Counter(
btree));
final long maxSignedInt = (long) Integer.MAX_VALUE;
final long minSignedInt = 0xFFFFFFFFL & (long) Integer.MIN_VALUE;
final long minusOneInt = 0xFFFFFFFFL & (long) -1;
// Verify the initial counter value.
assertEquals(0L, counter.get());
// Increment and get.
assertEquals(1L, counter.incrementAndGet());
// Set the underlying counter to max signed int.
btree.counter.set(maxSignedInt);
// Verify get().
assertEquals(maxSignedInt, counter.get());
// Increment and get (wraps to the long value whose low word is minSignedInt).
assertEquals(minSignedInt, counter.incrementAndGet());
// Increment. Should now be moving towards zero.
assertEquals(minSignedInt + 1L, counter.incrementAndGet());
// Increment. Should now be moving towards zero.
assertEquals(minSignedInt + 2L, counter.incrementAndGet());
/*
* Verify behavior as we approach the maximum value which can be
* expressed in an int32 local counter.
*/
// set the underlying counter.
btree.counter.set(minusOneInt - 1);
// Verify current value.
assertEquals(minusOneInt - 1, counter.get());
// Increment. Should now be a long whose lower word is minus one signed
// int.
assertEquals(minusOneInt, counter.incrementAndGet());
// Increment fails (counter overflow).
try {
counter.incrementAndGet();
fail("Expecting counter overflow");
} catch (RuntimeException ex) {
if (log.isInfoEnabled())
log.info("Ignoring expected exception: " + ex);
}
}
/**
* Unit tests for {@link PartitionedCounter} when the partition identifier
* is {@link Integer#MAX_VALUE}.
*/
public void test_partitionedCounter_pidMaxSignedInt() {
doPartitionedCounterTest(Integer.MAX_VALUE);
}
/**
* Unit tests for {@link PartitionedCounter} when the partition identifier
* is {@link Integer#MIN_VALUE}.
*/
public void test_partitionedCounter_pidMinSignedInt() {
doPartitionedCounterTest(Integer.MIN_VALUE);
}
/**
* Unit tests for {@link PartitionedCounter} when the partition identifier
* is <code>-1</code>.
*/
public void test_partitionedCounter_pidMinusOne() {
doPartitionedCounterTest(-1);
}
private void doPartitionedCounterTest(final int pid) {
final BTree btree = getBTree(3);
final ICounter counter = new PartitionedCounter(pid, new BTree.Counter(
btree));
final long maxSignedInt = (long) Integer.MAX_VALUE;
final long minusOneInt = 0xFFFFFFFFL & (long) -1;
// Verify the initial counter value.
assertSameCounter(pid, 0/*ctr*/, counter.get());
// Increment and get.
assertSameCounter(pid, 1/*ctr*/, counter.incrementAndGet());
// Set the underlying counter to max signed int.
btree.counter.set(maxSignedInt);
// Verify get().
assertSameCounter(pid, Integer.MAX_VALUE, counter.get());
// Increment and get (wraps to the long value whose low word is
// minSignedInt).
assertSameCounter(pid, Integer.MIN_VALUE, counter.incrementAndGet());
// Increment. Should now be moving towards zero.
assertSameCounter(pid, Integer.MIN_VALUE + 1, counter.incrementAndGet());
// Increment. Should now be moving towards zero.
assertSameCounter(pid, Integer.MIN_VALUE + 2, counter.incrementAndGet());
/*
* Verify behavior as we approach the maximum value which can be
* expressed in an int32 local counter.
*/
// set the underlying counter.
btree.counter.set(minusOneInt - 1);
// Verify current.
assertSameCounter(pid, -2, counter.get());
// Increment. Should now be a long whose lower word is minus one signed
// int.
assertSameCounter(pid, -1, counter.incrementAndGet());
// Increment fails (counter overflow).
try {
counter.incrementAndGet();
fail("Expecting counter overflow");
} catch (RuntimeException ex) {
if (log.isInfoEnabled())
log.info("Ignoring expected exception: " + ex);
}
}
private static void assertSameCounter(final int pid, final int ctr,
final long v0) {
// sanity check extract of pid.
assertEquals("pid", pid, TermIdEncoder.getPartitionId(v0));
// sanity check extract of ctr.
assertEquals("ctr", ctr, TermIdEncoder.getLocalCounter(v0));
final long v2 = BTree.PartitionedCounter.combine(pid, ctr);
assertEquals(v0, v2);
}
}