package voldemort.store.readonly; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.Random; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import voldemort.VoldemortException; import voldemort.utils.ByteUtils; /** * A base test for testing search strategies for the read-only store * * */ @RunWith(Parameterized.class) public class SearchStrategyTest { private SearchStrategy strategy; private int keyHashSize; @Parameters public static Collection<Object[]> configs() { return Arrays.asList(new Object[][] { { new BinarySearchStrategy(), 4 }, { new InterpolationSearchStrategy(), 4 }, { new BinarySearchStrategy(), 16 }, { new InterpolationSearchStrategy(), 16 } }); } public SearchStrategyTest(SearchStrategy strategy, int keyHashSize) { this.strategy = strategy; this.keyHashSize = keyHashSize; } @Test public void findNothingInEmptyIndex() { ByteBuffer index = makeIndex(new byte[][] {}, new int[] {}); assertKeysNotFound(index, key(-1, 2, -1, 2), key(9, 9, 9, 9)); } @Test public void testSingleton() { byte[] theKey = key(1, 2, 1, 2); int theVal = 42; ByteBuffer index = makeIndex(new byte[][] { theKey }, new int[] { theVal }); assertKeyFound(index, theKey, theVal); assertKeysNotFound(index, key(-1, 2, -1, 2), key(9, 9, 9, 9)); } @Test public void keyOutOfRange() { byte[] startKey = key(0, 1, 0, 1); byte[] endKey = key(Integer.MIN_VALUE, 0, 0, 0); ByteBuffer index = makeIndex(new byte[][] { startKey, endKey }, new int[] { 1, 2 }); assertKeyFound(index, startKey, 1); assertKeyFound(index, endKey, 2); assertKeysNotFound(index, key(-2, 0, 0, 0), key(-1, 1, 0, 0)); } @Test public void locationIsEndPoint() { byte[] startKey = key(0, 0, 0, 0); byte[] middleKey = key(5, 5, 5, 5); byte[] endKey = key(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE); ByteBuffer index = makeIndex(new byte[][] { startKey, middleKey, endKey }, new int[] { 1, 2, 3 }); assertKeyFound(index, startKey, 1); assertKeyFound(index, middleKey, 2); assertKeyFound(index, endKey, 3); assertKeysNotFound(index, key(-1, 2, -1, 2), key(9, 9, 9, 9)); } @Test public void testRandomValues() { Random rand = new Random(48534543); int size = 100; for(int iter = 0; iter < 10; iter++) { byte[][] keys = new byte[size][]; int[] values = new int[size]; for(int i = 0; i < size; i++) { byte[] key = new byte[keyHashSize]; rand.nextBytes(key); values[i] = rand.nextInt(1000000); keys[i] = key; } ByteBuffer index = makeIndex(keys, values); // test finding keys for(int i = 0; i < size; i++) assertKeyFound(index, keys[i], values[i]); for(int i = 0; i < 10; i++) { byte[] key = new byte[keyHashSize]; assertKeysNotFound(index, key); } } } public void print(byte[][] keys, int[] positions) { for(int i = 0; i < keys.length; i++) { System.out.println(ByteUtils.toHexString(keys[i]) + "\t" + positions[i]); } } public void print(ByteBuffer buffer, int size) { buffer.position(0); for(int i = 0; i < size; i++) { byte[] key = new byte[16]; buffer.get(key); int position = buffer.getInt(); System.out.println(ByteUtils.toHexString(key) + "\t" + position); } } public ByteBuffer makeIndex(byte[][] keys, int[] positions) { assertEquals(keys.length, positions.length); Map<byte[], Integer> m = new HashMap<byte[], Integer>(); for(int i = 0; i < keys.length; i++) m.put(keys[i], positions[i]); byte[][] copy = keys.clone(); Arrays.sort(copy, new Comparator<byte[]>() { public int compare(byte[] b1, byte[] b2) { return ByteUtils.compare(b1, b2); } }); ByteBuffer buffer = ByteBuffer.allocate((keyHashSize + ByteUtils.SIZE_OF_INT) * copy.length); for(int i = 0; i < copy.length; i++) { buffer.put(copy[i]); buffer.putInt(m.get(copy[i])); } return buffer; } public byte[] key(int v1, int v2, int v3, int v4) { byte[] bytes = new byte[keyHashSize]; switch(keyHashSize) { case 16: ByteUtils.writeInt(bytes, v1, 0); ByteUtils.writeInt(bytes, v2, 4); ByteUtils.writeInt(bytes, v3, 8); ByteUtils.writeInt(bytes, v4, 12); break; case 4: ByteUtils.writeInt(bytes, v1, 0); break; default: throw new VoldemortException("Key hash size " + keyHashSize + " not supported"); } return bytes; } public void assertKeyFound(ByteBuffer buffer, byte[] key, int expected) { int found = strategy.indexOf(buffer, key, buffer.limit()); assertTrue("Failed to find correct key " + key, found != -1); assertEquals(expected, found); } public void assertKeysNotFound(ByteBuffer buffer, byte[]... keys) { for(byte[] key: keys) { assertTrue("Failed to find key " + key, strategy.indexOf(buffer, key, buffer.limit()) == -1); } } }