/**
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 Sep 4, 2007
*/
package com.bigdata.rawstore;
import java.util.Random;
import junit.framework.TestCase;
/**
* Test suite for {@link WormAddressManager}.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*/
public class TestWormAddressManager extends TestCase {
Random r = new Random();
/**
*
*/
public TestWormAddressManager() {
super();
}
public TestWormAddressManager(String name) {
super(name);
}
// /**
// * verify that the constructor correctly interprets is parameter as the #of
// * offset bits.
// */
// public void test_ctor_default() {
//
// WormAddressManager am = new WormAddressManager();
//
// assertEquals("offsetBits", WormAddressManager.DEFAULT_OFFSET_BITS,
// am.getOffsetBits() );
//
// assertEquals("maxOffset", 4 * Bytes.terabyte - 1, am.getMaxOffset());
//
// assertEquals("maxByteCount", 4 * Bytes.megabyte - 1, am.getMaxByteCount());
//
// }
/**
* Test of constructor when splitting the long integer into two 32-bit
* unsigned integer components (offset and #bytes).
*/
public void test_ctor_32bits() {
WormAddressManager am = new WormAddressManager(32);
assertEquals("offsetBits",32,am.offsetBits);
assertEquals("byteCountBits",32,am.byteCountBits);
assertEquals("maxOffset",0xffffffffL,am.maxOffset);
/*
* Note: 32 _unsigned_ bits can actually store more than can fit into a
* signed 32-bit integer so we are modeling the limit as a long integer.
*/
assertEquals("maxByteCount",0xffffffffL,am.maxByteCount);
// private static final transient long OFFSET_MASK = 0xffffffff00000000L;
assertEquals("offsetMask",0xffffffff00000000L,am.offsetMask);
// private static final transient long NBYTES_MASK = 0x00000000ffffffffL;
assertEquals("byteCountMask",0x00000000ffffffffL,am.byteCountMask);
}
/**
* Test of constructor when splitting the long integer into a 48-bit
* unsigned integer (offset) and a 16-bit unsigned integer (#bytes).
*/
public void test_ctor_48bits() {
WormAddressManager am = new WormAddressManager(48);
assertEquals("offsetBits",48,am.offsetBits);
assertEquals("byteCountBits",16,am.byteCountBits);
assertEquals("maxOffset",0xffffffffffffL,am.maxOffset);
assertEquals("maxByteCount",0xffffL,am.maxByteCount);
assertEquals("offsetMask",0xffffffffffff0000L,am.offsetMask);
assertEquals("byteCountMask",0x000000000000ffffL,am.byteCountMask);
}
public void test_ctor_correctRejection() {
try {
new WormAddressManager(0);
fail("Expecting: "+IllegalArgumentException.class);
} catch(IllegalArgumentException ex) {
System.err.println("Ignoring expected exception: "+ex);
}
try {
new WormAddressManager(-1);
fail("Expecting: "+IllegalArgumentException.class);
} catch(IllegalArgumentException ex) {
System.err.println("Ignoring expected exception: "+ex);
}
try {
new WormAddressManager(WormAddressManager.MIN_OFFSET_BITS-1);
fail("Expecting: "+IllegalArgumentException.class);
} catch(IllegalArgumentException ex) {
System.err.println("Ignoring expected exception: "+ex);
}
try {
new WormAddressManager(WormAddressManager.MAX_OFFSET_BITS+1);
fail("Expecting: "+IllegalArgumentException.class);
} catch(IllegalArgumentException ex) {
System.err.println("Ignoring expected exception: "+ex);
}
/*
* and verify that the end points of the range are legal.
*/
new WormAddressManager(WormAddressManager.MIN_OFFSET_BITS);
new WormAddressManager(WormAddressManager.MAX_OFFSET_BITS);
}
/**
* Verify that {@link WormAddressManager#toAddr(int, long)} will reject a zero
* byte count or a zero offset since the value <code>0L</code> is reserved
* to represent a null reference.
*/
public void test_canNotConstructNULLs() {
for(int i=WormAddressManager.MIN_OFFSET_BITS; i<=WormAddressManager.MAX_OFFSET_BITS; i++) {
WormAddressManager am = new WormAddressManager( i );
try {
am.toAddr(0/*nbytes*/, 1L/*offset*/);
} catch(IllegalArgumentException ex) {
System.err.println("Ignoring expected exception: "+ex);
}
try {
am.toAddr(1/*nbytes*/, 0L/*offset*/);
} catch(IllegalArgumentException ex) {
System.err.println("Ignoring expected exception: "+ex);
}
}
}
/**
* Test of encoding and decoding addressed with a small set of 32 bit
* offsets.
*/
public void test_get_32() {
doTestGet(32, 1000);
}
/**
* Test of encoding and decoding addressed with a small set of 48 bit
* offsets.
*/
public void test_get_48() {
doTestGet(48, 1000);
}
/**
* Test of encoding and decoding addresses with a set of addresses selected
* from each of the legal values of the offsetBits.
*/
public void test_get() {
for (int i = WormAddressManager.MIN_OFFSET_BITS; i <= WormAddressManager.MAX_OFFSET_BITS; i++) {
doTestGet(i, 10000);
}
}
// /**
// * Test of packing and unpacking addresses with a small set of addresses
// * using 32 bit offsets.
// */
// public void test_packUnpack_32() throws IOException {
//
// doTestPackUnpack(32, 100);
//
// }
//
// /**
// * Test of packing and unpacking addresses with a small set of addresses
// * using 48 bit offsets.
// */
// public void test_packUnpack_48() throws IOException {
//
// doTestPackUnpack(48, 100);
//
// }
//
// /**
// * Test of packing and unpacking addresses with a set of addresses selected
// * from each of the legal values of the offsetBits.
// */
// public void test_packUnpack() throws IOException {
//
// for (int i = WormAddressManager.MIN_OFFSET_BITS; i <= WormAddressManager.MAX_OFFSET_BITS; i++) {
//
// doTestPackUnpack(i, 10000);
//
// }
//
// }
/**
* Helper performs random tests of {@link WormAddressManager#toAddr(int, long)}
* and is intended to verify both consistency of encoding and decoding and
* correct rejection when encoding.
*
* @param offsetBits
*/
public void doTestGet(int offsetBits,int limit) {
WormAddressManager am = new WormAddressManager(offsetBits);
for(int i=0; i<limit; i++) {
/*
* next #of bytes in [0:maxByteCount], but never more tha
* Integer.MAX_VALUE bytes.
*/
final int nbytes = r
.nextInt((am.byteCountBits >= 32 ? Integer.MAX_VALUE
: (int) am.maxByteCount));
/*
* Any long value, but when 0, negative, or too large then we will
* check for correct rejection.
*/
final long offset = r.nextLong();
if (nbytes < 0 || offset > am.maxOffset || offset < 0L) {
/*
* Check for correct rejection.
*/
try {
am.toAddr(nbytes, offset);
fail("Expecting: " + IllegalArgumentException.class
+ " (nbytes=" + nbytes + ", offset=" + offset + ")");
} catch(IllegalArgumentException ex) {
// Ignoring expected exception.
}
} else {
long addr = am.toAddr(nbytes, offset);
assertEquals("nbytes",nbytes,am.getByteCount(addr));
assertEquals("offset",offset,am.getOffset(addr));
}
}
}
// /**
// * Helper method verifies (de-)serialization of addresses.
// *
// * @param offsetBits
// *
// * @throws IOException
// */
// public void doTestPackUnpack(int offsetBits,int limit) throws IOException {
//
// WormAddressManager am = new WormAddressManager(offsetBits);
//
// /*
// * Generate an array of valid encoded addresses, including a bunch that
// * are NULLs.
// */
// long[] addrs = new long[limit];
//
// for(int i=0; i<limit; i++) {
//
// long addr;
//
// if (r.nextInt(100) < 5) {
//
// // 5% are NULLs.
// addr = 0L;
//
// } else {
//
// addr = nextAddr(r,am);
//
//// System.err.print(".");
//
// }
//
// addrs[i] = addr;
//
// }
//
//// System.err.println("Generated "+limit+" addresses.");
//
// /*
// * Pack the generated addresses.
// */
// final byte[] packed;
// {
//
// /*
// * The result should tend to be significantly smaller than directly
// * writing 8 bytes per address into the store, but of course that
// * depends on the actual addresses.
// */
// ByteArrayOutputStream baos = new ByteArrayOutputStream(limit*Bytes.SIZEOF_LONG);
//
// DataOutputStream os = new DataOutputStream(baos);
//
// for (int i = 0; i < limit; i++) {
//
// long addr = addrs[i];
//
// try {
//
// am.packAddr(os, addr);
//
// } catch(IllegalArgumentException ex) {
//
// fail("Could not pack addr="+addr+"("+am.toString(addr)+") : "+ex);
//
// }
//
// }
//
// os.flush();
//
// packed = baos.toByteArray();
//
// }
//
// /*
// * @todo compute and show the compression ratio but note that it will
// * not be good when the addresses are choosen randomly.
// */
//// System.err.println("Compression ratio: "+(limit))
//
// /*
// * Verify that the addresses unpack correctly.
// */
// {
//
// DataInputStream in = new DataInputStream(new ByteArrayInputStream(
// packed));
//
// for (int i = 0; i < limit; i++) {
//
// long addr = am.unpackAddr(in);
//
// assertEquals(addrs[i],addr);
//
// }
//
// }
//
// }
/**
* Returns a legal random address and NULL 5% of the time.
*/
static public long nextAddr(Random r,WormAddressManager am) {
if(r.nextInt(100)<5) return 0L;
return nextNonZeroAddr(r,am);
}
/**
* Returns a legal random non-NULL address.
*/
static public long nextNonZeroAddr(Random r,WormAddressManager am) {
final int nbytes = nextNonZeroByteCount(r,am);
final long offset = nextNonZeroOffset(r,am);
assertEncodeDecode(am,nbytes,offset);
final long addr = am.toAddr(nbytes, offset);
return addr;
}
/**
* Returns a legal random non-NULL address that does not extend as far as
* <i>limit</i>.
*
* @param limit
* The first byte that must not be covered by the returned
* address.
*
* @todo this does not excercise the entire possible range addresses that
* would satisify the contract of this method.
*/
static public long nextNonZeroAddr(Random r,WormAddressManager am, long limit) {
final long offset = r.nextInt((int)Math.min(Integer.MAX_VALUE,limit));
final int nbytes = r.nextInt((int) Math.min(am.getMaxByteCount(), Math
.min(Integer.MAX_VALUE, limit - offset)));
assert offset + nbytes < limit;
assertEncodeDecode(am,nbytes,offset);
final long addr = am.toAddr(nbytes, offset);
return addr;
}
/**
* Verify that we can encode and decode that address.
*
* @param am
* The address manager.
* @param nbytes
* The ground truth byte count.
* @param offset
* The ground truth byte offset.
*/
static void assertEncodeDecode(WormAddressManager am, int nbytes,
long offset) {
final long addr = am.toAddr(nbytes, offset);
if (nbytes != am.getByteCount(addr)) {
fail("offsetBits=" + am.offsetBits + ", addr=" + addr
+ ", expected nbytes=" + nbytes + ", actual="
+ am.getByteCount(addr) + ", offset=" + offset);
}
if (offset != am.getOffset(addr)) {
fail("offsetBits=" + am.offsetBits + ", addr=" + addr
+ ", expected offset=" + offset + ", actual="
+ am.getOffset(addr) + ", nbytes=" + nbytes);
}
}
/**
* Next random byte count in [0:maxByteCount], but never more than
* {@link Integer#MAX_VALUE} bytes and zero (0) 5% of the time.
*/
static public int nextByteCount(Random r, WormAddressManager am) {
if(r.nextInt(100)<5) {
return 0;
}
final int nbytes = nextNonZeroByteCount(r, am);
return nbytes;
}
/**
* Next random legal non-zero byte count less than maxByteCount and never
* more than {@link Integer#MAX_VALUE} bytes.
*/
static public int nextNonZeroByteCount(Random r, WormAddressManager am) {
int nbytes = 0;
while(nbytes==0) {
nbytes = r
.nextInt((am.byteCountBits >= 32 ? Integer.MAX_VALUE
: (int) am.maxByteCount));
}
return nbytes;
}
/**
* Next random byte offset and <code>0L</code> 5% of the time.
*/
static public long nextOffset(Random r, WormAddressManager am) {
if(r.nextInt()<5) return 0L;
return nextNonZeroOffset(r, am);
}
/**
* Next non-zero random byte offset less than maxOffset.
*/
static public long nextNonZeroOffset(Random r, WormAddressManager am) {
long offset = 0L;
while (offset == 0L) {
/*
* start with any random long value and then mask off the bits that
* are not allowed and shift down to to cover the zeros that leaves
* in the lower order bits.
*/
long offset1 = r.nextLong();
long offset2 = offset1 & am.offsetMask;
offset = offset2 >>> am.byteCountBits;
}
assert offset <= am.maxOffset;
return offset;
}
/**
* One of a series of tests of encoding and decoding addresses that have
* proven themselves to be edge conditions in the code.
*/
public void test_encodeDecode_offsetBits32_01() {
WormAddressManager am = new WormAddressManager(32);
final int nbytes = 1537183690;
final long offset = 3154105902L;
final long addr = am.toAddr(nbytes, offset);
assertEquals("offset",offset,am.getOffset(addr));
assertEquals("nbytes",nbytes,am.getByteCount(addr));
}
public void test_encodeDecode_offsetBits48_01() {
WormAddressManager am = new WormAddressManager(48);
final int nbytes = 55065;
final long offset = 227799501816710L;
final long addr = am.toAddr(nbytes, offset);
assertEquals("offset",offset,am.getOffset(addr));
assertEquals("nbytes",nbytes,am.getByteCount(addr));
}
public void test_static_getMaxByteCount() {
/*
* edge case - 32 unsigned bits can not be represented in a Java signed
* integer so the max byte count is limited by the API to the maximum
* value of a signed integer.
*/
assertEquals("maxByteCount", Integer.MAX_VALUE, WormAddressManager
.getMaxByteCount(32));
/*
* 31 unsigned bits can represent the Java signed int maximum value.
*/
assertEquals("maxByteCount", Integer.MAX_VALUE, WormAddressManager
.getMaxByteCount(33));
/*
* And check a few other data points.
*/
assertEquals("maxByteCount", 4194304, WormAddressManager
.getMaxByteCount(42));
assertEquals("maxByteCount", 1024, WormAddressManager
.getMaxByteCount(54));
}
/**
* Test that {@link IAddressManager#toAddr(int, long)} will correctly reject
* requests where the byte count is invalid.
*/
public void test_toAddr_correctRejection_byteCount() {
WormAddressManager am = new WormAddressManager(48);
// correct rejection of a negative byte count.
try {
am.toAddr(-1,0L);
fail("Expecting: "+IllegalArgumentException.class);
} catch(IllegalArgumentException ex) {
System.err.println("Ingoring expected exception: "+ex);
}
/*
* correct acceptance of the maximum byte count.
*/
am.toAddr(am.getMaxByteCount(),0L);
// correct rejection of the maximum byte count plus one.
try {
am.toAddr(am.getMaxByteCount()+1,0L);
fail("Expecting: "+IllegalArgumentException.class);
} catch(IllegalArgumentException ex) {
System.err.println("Ingoring expected exception: "+ex);
}
}
/**
* Test that {@link IAddressManager#toAddr(int, long)} will correctly reject
* requests where the byte offset is invalid.
*/
public void test_toAddr_correctRejection_byteOffset() {
WormAddressManager am = new WormAddressManager(48);
// correct rejection of a negative byte offset.
try {
am.toAddr(1,-1L);
fail("Expecting: "+IllegalArgumentException.class);
} catch(IllegalArgumentException ex) {
System.err.println("Ingoring expected exception: "+ex);
}
/*
* correct acceptance of the maximum byte offset.
*/
am.toAddr(1,am.getMaxOffset());
// correct rejection of the maximum byte offset plus one.
try {
am.toAddr(1,am.getMaxOffset()+1);
fail("Expecting: "+IllegalArgumentException.class);
} catch(IllegalArgumentException ex) {
System.err.println("Ingoring expected exception: "+ex);
}
}
}