/** 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 Jan 22, 2007 */ package com.bigdata.htree.raba; import java.io.IOException; import junit.framework.TestCase2; import com.bigdata.btree.raba.IRaba; import com.bigdata.btree.raba.ReadOnlyValuesRaba; import com.bigdata.htree.HTree; import com.bigdata.io.DataInputBuffer; /** * Test suite for {@link MutableKeyBuffer}. * * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> * @version $Id$ */ public class TestMutableKeyBuffer extends TestCase2 { /** * */ public TestMutableKeyBuffer() { } /** * @param name */ public TestMutableKeyBuffer(String name) { super(name); } /** * Unit tests for constructor accepting only the capacity of the buffer. */ public void test_ctor_capacity_correct_rejection() { // Capacity must be positive and a (positive) power of two (not 2^0). try { new MutableKeyBuffer(0); fail("Expecting: " + IllegalArgumentException.class); } catch (IllegalArgumentException ex) { // ignored. } try { new MutableKeyBuffer(-1); fail("Expecting: " + IllegalArgumentException.class); } catch (IllegalArgumentException ex) { // ignored. } try { new MutableKeyBuffer(1); fail("Expecting: " + IllegalArgumentException.class); } catch (IllegalArgumentException ex) { // ignored. } try { new MutableKeyBuffer(3); fail("Expecting: " + IllegalArgumentException.class); } catch (IllegalArgumentException ex) { // ignored. } try { new MutableKeyBuffer(17); fail("Expecting: " + IllegalArgumentException.class); } catch (IllegalArgumentException ex) { // ignored. } } /** * These are legal invocations. */ public void test_ctor_capacity_validInvocations() { new MutableKeyBuffer(2); new MutableKeyBuffer(4); new MutableKeyBuffer(8); new MutableKeyBuffer(16); } /** * Check the post-conditions for a legal invocation of the constructor. */ public void test_ctor_capacity_postConditions() { final int m = 2; final MutableKeyBuffer t = new MutableKeyBuffer(m); assertEquals(0, t.size()); assertEquals(m, t.capacity()); assertNotNull(t.keys); assertEquals(m, t.keys.length); for (int i = 0; i < m; i++) { assertNull(t.keys[i]); } } public void test_ctor_wrap_correct_rejection() { try { new MutableKeyBuffer(0/* nkeys */, (byte[][]) null); fail("Expecting: " + IllegalArgumentException.class); } catch (IllegalArgumentException ex) { // ignored. } // not a valid power of 2. try { new MutableKeyBuffer(0/* nkeys */, new byte[1][]); fail("Expecting: " + IllegalArgumentException.class); } catch (IllegalArgumentException ex) { // ignored. } // not a valid power of 2. try { new MutableKeyBuffer(0/* nkeys */, new byte[3][]); fail("Expecting: " + IllegalArgumentException.class); } catch (IllegalArgumentException ex) { // ignored. } // size is negative try { new MutableKeyBuffer(-1/* nkeys */, new byte[2][]); fail("Expecting: " + IllegalArgumentException.class); } catch (IllegalArgumentException ex) { // ignored. } // size is too large try { new MutableKeyBuffer(3/* nkeys */, new byte[2][]); fail("Expecting: " + IllegalArgumentException.class); } catch (IllegalArgumentException ex) { // ignored. } } /** * Unit test constructor wrapping the caller's data. For this constructor, * the new instance should use the same references and the capacity should * be the dimension of the source byte[][]. */ public void test_ctor_wrap_postConditions() { final byte[] k0 = null; final byte[] k1 = new byte[]{1}; final byte[] k2 = new byte[]{2}; final byte[] k3 = null; final byte[][] keys = new byte[][] { k0,k1,k2,k3 }; final MutableKeyBuffer buf = new MutableKeyBuffer(2/* nkeys */, keys); assertEquals(keys.length, buf.capacity()); assertEquals(2, buf.size()); // same data assertEquals(k0, buf.keys[0]); assertEquals(k1, buf.keys[1]); assertEquals(k2, buf.keys[2]); assertEquals(k3, buf.keys[3]); // same references. assertTrue(k0 == buf.keys[0]); assertTrue(k1 == buf.keys[1]); assertTrue(k2 == buf.keys[2]); assertTrue(k3 == buf.keys[3]); // same byte[][] reference. assertTrue(keys == buf.keys); } public void test_ctor_copy_correct_rejection() { try { new MutableKeyBuffer(null); fail("Expecting: " + IllegalArgumentException.class); } catch (IllegalArgumentException ex) { // ignored. } } /** * Unit test the copy constructor. */ public void test_ctor_copy() { final byte[] k0 = null; final byte[] k1 = new byte[]{1}; final byte[] k2 = new byte[]{2}; final byte[] k3 = null; final byte[][] keys = new byte[][] { k0,k1,k2,k3 }; final MutableKeyBuffer src = new MutableKeyBuffer(2/* nkeys */, keys); final MutableKeyBuffer buf = new MutableKeyBuffer(src); assertEquals(keys.length, buf.capacity()); assertEquals(2, buf.size()); // same data assertEquals(k0, buf.keys[0]); assertEquals(k1, buf.keys[1]); assertEquals(k2, buf.keys[2]); assertEquals(k3, buf.keys[3]); // same references. assertTrue(k0 == buf.keys[0]); assertTrue(k1 == buf.keys[1]); assertTrue(k2 == buf.keys[2]); assertTrue(k3 == buf.keys[3]); // NOT the same byte[][] reference. assertTrue(keys != buf.keys); } public void test_ctor_raba_with_explicit_capacity_correct_rejection() { final byte[] k0 = null; final byte[] k1 = new byte[]{1}; final byte[] k2 = new byte[]{2}; final byte[] k3 = null; final byte[][] keys = new byte[][] { k0,k1,k2,k3 }; final IRaba src = new ReadOnlyValuesRaba(keys); // raba may not be null try { new MutableKeyBuffer(2/* capacity */, (IRaba) null/* src */); fail("Expecting: " + IllegalArgumentException.class); } catch (IllegalArgumentException ex) { // ignored. } // capacity must be 2^n where n is positive. try { new MutableKeyBuffer(3/* capacity */, (IRaba) src); fail("Expecting: " + IllegalArgumentException.class); } catch (IllegalArgumentException ex) { // ignored. } // capacity must not be LT the capacity of the raba. try { new MutableKeyBuffer(2/* capacity */, (IRaba) src); fail("Expecting: " + IllegalArgumentException.class); } catch (IllegalArgumentException ex) { // ignored. } } /** * Unit test the constructor variant which accepts an {@link IRaba} and * an explicitly given capacity. */ public void test_ctor_raba_with_explicit_capacity() { final byte[] k0 = new byte[]{0}; final byte[] k1 = new byte[]{1}; final byte[] k2 = new byte[]{2}; final byte[] k3 = new byte[]{3}; final byte[][] keys = new byte[][] { k0,k1,k2,k3 }; /* * Note: The ReadOnlyValuesRaba assumes that the 1st size values have * valid data. Thus it is not really suited to sparse random access * pattern of the HTree keys and values. */ final IRaba src = new ReadOnlyValuesRaba(4/*size*/,keys); final MutableKeyBuffer buf = new MutableKeyBuffer(8/* capacity */, src); assertEquals(8,buf.capacity()); // NB: As given! assertEquals(4, buf.size()); // same data assertEquals(k0, buf.keys[0]); assertEquals(k1, buf.keys[1]); assertEquals(k2, buf.keys[2]); assertEquals(k3, buf.keys[3]); assertNull(buf.keys[4]); assertNull(buf.keys[5]); assertNull(buf.keys[6]); assertNull(buf.keys[7]); // NOT the same byte[][] reference. assertTrue(keys != buf.keys); } /** * The keys of the {@link HTree} do not have any of the requirements of the * B+Tree keys. The keys in a bucket page may contain duplicates but are now * ordered and therefore searchable. Therefore this class reports * <code>true</code> for {@link IRaba#isKeys()}. */ public void test_isKeys() { // Note: Must be a power of 2 for an HTree. final int m = 4; final MutableKeyBuffer kbuf = new MutableKeyBuffer(m); assertTrue(kbuf.isKeys()); } /** * The various methods "add()" methods are not supported because they do not * specify the index of the tuple and we need to have that since the tuples * are not dense and the bucket page is divided into buddy bucket regions * which must be managed separately. * * @throws IOException */ public void test_add_not_supported() throws IOException { // Note: Must be a power of 2 for an HTree. final int m = 4; final MutableKeyBuffer kbuf = new MutableKeyBuffer(m); try { kbuf.add(new byte[] {1,2}); fail("Expecting: " + UnsupportedOperationException.class); } catch (UnsupportedOperationException ex) { if (log.isInfoEnabled()) log.info("Ignoring expected exception: " + ex); } try { kbuf.add(new byte[] {1,2},0/*off*/,1/*len*/); fail("Expecting: " + UnsupportedOperationException.class); } catch (UnsupportedOperationException ex) { if (log.isInfoEnabled()) log.info("Ignoring expected exception: " + ex); } try { kbuf.add(new DataInputBuffer(new byte[2]), 1/* len */); fail("Expecting: " + UnsupportedOperationException.class); } catch (UnsupportedOperationException ex) { if (log.isInfoEnabled()) log.info("Ignoring expected exception: " + ex); } } /** * {@link IRaba#search(byte[])} is now supported because a BucketPage * contains ordered keys. Note that there can be * duplicate keys within a buddy hash table. */ public void test_search_not_supported() { // Note: Must be a power of 2 for an HTree. final int m = 4; final MutableKeyBuffer kbuf = new MutableKeyBuffer(m); try { kbuf.search(new byte[]{1,2}); } catch (UnsupportedOperationException ex) { fail("Search should be supported if keys are ordered"); } } /** * Test for key mutation operations (adding, removing, etc). */ public void test_mutation() { // Note: Must be a power of 2 for an HTree. final int m = 4; final MutableKeyBuffer kbuf = new MutableKeyBuffer(m); if (log.isInfoEnabled()) log.info(kbuf.toString()); assertEquals("keys.length", m, kbuf.keys.length); assertEquals("maxKeys", m, kbuf.capacity()); assertEquals("nkeys", 0, kbuf.nkeys); assertNull(kbuf.keys[0]); final byte[] k0 = new byte[] { 1, 2, 0 }; final byte[] k1 = new byte[] { 1, 2, 1 }; final byte[] k2 = new byte[] { 1, 2, 2 }; final byte[] k3 = new byte[] { 1, 2, 3 }; kbuf.set(0/* index */, k0); if (log.isInfoEnabled()) log.info(kbuf.toString()); assertEquals("nkeys", 1, kbuf.nkeys); assertFalse(kbuf.isFull()); assertEquals(k0, kbuf.keys[0]); kbuf.set(2/*index*/,k2); // Note leaves a "hole" at index 1. if (log.isInfoEnabled()) log.info(kbuf.toString()); assertEquals("nkeys", 2, kbuf.nkeys); assertFalse(kbuf.isFull()); assertEquals(k0, kbuf.keys[0]); assertEquals(k2, kbuf.keys[2]); kbuf.set(3/*index*/,k3); if (log.isInfoEnabled()) log.info(kbuf.toString()); assertEquals("nkeys", 3, kbuf.nkeys); assertFalse(kbuf.isFull()); assertEquals(k0, kbuf.keys[0]); assertEquals(k2, kbuf.keys[2]); assertEquals(k3, kbuf.keys[3]); kbuf.set(1/*index*/,k1); if (log.isInfoEnabled()) log.info(kbuf.toString()); assertEquals("nkeys", 4, kbuf.nkeys); assertTrue(kbuf.isFull()); assertEquals(k0, kbuf.keys[0]); assertEquals(k1, kbuf.keys[1]); assertEquals(k2, kbuf.keys[2]); assertEquals(k3, kbuf.keys[3]); // remove the 1st key, leaving 3 keys. assertEquals("nkeys", 3, kbuf.remove(0)); if (log.isInfoEnabled()) log.info(kbuf.toString()); assertEquals("nkeys", 3, kbuf.nkeys); assertFalse(kbuf.isFull()); assertEquals(k1, kbuf.keys[0]); assertEquals(k2, kbuf.keys[1]); assertEquals(k3, kbuf.keys[2]); assertNull(kbuf.keys[3]); // remove the last key, leaving 2 keys. assertEquals("nkeys", 2, kbuf.remove(2)); if (log.isInfoEnabled()) log.info(kbuf.toString()); assertEquals("nkeys", 2, kbuf.nkeys); assertFalse(kbuf.isFull()); assertEquals(k1, kbuf.keys[0]); assertEquals(k2, kbuf.keys[1]); assertNull(kbuf.keys[2]); assertNull(kbuf.keys[3]); // insert a key in the 1st position (3 keys in the buffer). kbuf.insert(0, k1); if (log.isInfoEnabled()) log.info(kbuf.toString()); assertEquals("nkeys", 3, kbuf.nkeys); assertFalse(kbuf.isFull()); assertEquals(k1, kbuf.keys[0]); // note: duplicate key. assertEquals(k1, kbuf.keys[1]); assertEquals(k2, kbuf.keys[2]); assertNull(kbuf.keys[3]); // set a key in the last position (buffer is full again). kbuf.set(3, k2); if (log.isInfoEnabled()) log.info(kbuf.toString()); assertEquals("nkeys", 4, kbuf.nkeys); assertTrue(kbuf.isFull()); assertEquals(k1, kbuf.keys[0]); assertEquals(k1, kbuf.keys[1]); assertEquals(k2, kbuf.keys[2]); assertEquals(k2, kbuf.keys[3]); // note: another duplicate key. } }