blob: 94ceca382e880e35eff9735796b35f50a559497f [file] [log] [blame]
/*=========================================================================
* Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
* This product is protected by U.S. and international copyright
* and intellectual property laws. Pivotal products are covered by
* one or more patents listed at http://www.pivotal.io/patents.
*=========================================================================
*/
package com.gemstone.gemfire.internal.cache.persistence.soplog;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.TreeMap;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import junit.framework.TestCase;
import com.gemstone.gemfire.internal.cache.persistence.soplog.SortedReader.SortedIterator;
import com.gemstone.gemfire.internal.cache.persistence.soplog.SortedReader.SortedStatistics;
public abstract class SortedReaderTestCase extends TestCase {
private NavigableMap<byte[], byte[]> data;
protected SortedReader<ByteBuffer> reader;
public static void assertEquals(byte[] expected, ByteBuffer actual) {
assertEquals(expected.length, actual.remaining());
for (int i = 0; i < expected.length; i++) {
assertEquals(expected[i], actual.get(actual.position() + i));
}
}
public void testComparator() {
assertNotNull(reader.getComparator());
}
public void testStatistics() throws IOException {
SortedStatistics stats = reader.getStatistics();
try {
assertEquals(data.size(), stats.keyCount());
assertTrue(Arrays.equals(data.firstKey(), stats.firstKey()));
assertTrue(Arrays.equals(data.lastKey(), stats.lastKey()));
int keySize = 0;
int valSize = 0;
for (Entry<byte[], byte[]> entry : data.entrySet()) {
keySize += entry.getKey().length;
valSize += entry.getValue().length;
}
double avgKey = keySize / data.size();
double avgVal = valSize / data.size();
assertEquals(avgVal, stats.avgValueSize());
assertEquals(avgKey, stats.avgKeySize());
} finally {
stats.close();
}
}
public void testMightContain() throws IOException {
for (byte[] key : data.keySet()) {
assertTrue(reader.mightContain(key));
}
}
public void testRead() throws IOException {
for (byte[] key : data.keySet()) {
assertEquals(data.get(key), reader.read(key));
}
}
public void testScan() throws IOException {
SortedIterator<ByteBuffer> scan = reader.scan();
try {
Iterator<Entry<byte[], byte[]>> iter = data.entrySet().iterator();
doIter(scan, iter);
} finally {
scan.close();
}
}
public void testMultithreadScan() throws Exception {
int threads = 10;
ExecutorService exec = Executors.newFixedThreadPool(threads);
List<Callable<Boolean>> tasks = new ArrayList<Callable<Boolean>>();
for (int i = 0; i < threads; i++) {
tasks.add(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
testScan();
return true;
}
});
}
int i = 0;
while (i++ < 1000) {
for (Future<Boolean> ft : exec.invokeAll(tasks)) {
assertTrue(ft.get());
}
}
}
public void testScanReverse() throws IOException {
SortedIterator<ByteBuffer> scan = reader.withAscending(false).scan();
try {
Iterator<Entry<byte[], byte[]>> iter = data.descendingMap().entrySet().iterator();
doIter(scan, iter);
} finally {
scan.close();
}
}
public void testHead() throws IOException {
byte[] split = wrapInt(50);
SortedIterator<ByteBuffer> scan = reader.head(split, true);
try {
Iterator<Entry<byte[], byte[]>> iter = data.headMap(split, true).entrySet().iterator();
doIter(scan, iter);
} finally {
scan.close();
}
scan = reader.head(split, false);
try {
Iterator<Entry<byte[], byte[]>> iter = data.headMap(split, false).entrySet().iterator();
doIter(scan, iter);
} finally {
scan.close();
}
}
public void testTail() throws IOException {
byte[] split = wrapInt(50);
SortedIterator<ByteBuffer> scan = reader.tail(split, true);
try {
Iterator<Entry<byte[], byte[]>> iter = data.tailMap(split, true).entrySet().iterator();
doIter(scan, iter);
} finally {
scan.close();
}
scan = reader.tail(split, false);
try {
Iterator<Entry<byte[], byte[]>> iter = data.tailMap(split, false).entrySet().iterator();
doIter(scan, iter);
} finally {
scan.close();
}
}
public void testScanWithBounds() throws IOException {
byte[] lhs = wrapInt(10);
byte[] rhs = wrapInt(90);
// [lhs,rhs)
SortedIterator<ByteBuffer> scan = reader.scan(lhs, rhs);
try {
Iterator<Entry<byte[], byte[]>> iter = data.subMap(lhs, rhs).entrySet().iterator();
doIter(scan, iter);
} finally {
scan.close();
}
// (lhs,rhs)
scan = reader.scan(lhs, false, rhs, false);
try {
Iterator<Entry<byte[], byte[]>> iter = data.subMap(lhs, false, rhs, false).entrySet().iterator();
doIter(scan, iter);
} finally {
scan.close();
}
// [lhs,rhs]
scan = reader.scan(lhs, true, rhs, true);
try {
Iterator<Entry<byte[], byte[]>> iter = data.subMap(lhs, true, rhs, true).entrySet().iterator();
doIter(scan, iter);
} finally {
scan.close();
}
}
public void testReverseScanWithBounds() throws IOException {
data = data.descendingMap();
byte[] rhs = wrapInt(10);
byte[] lhs = wrapInt(90);
SortedReader<ByteBuffer> rev = reader.withAscending(false);
// [rhs,lhs)
SortedIterator<ByteBuffer> scan = rev.scan(lhs, rhs);
try {
Iterator<Entry<byte[], byte[]>> iter = data.subMap(lhs, rhs).entrySet().iterator();
doIter(scan, iter);
} finally {
scan.close();
}
// (rhs,lhs)
scan = rev.scan(lhs, false, rhs, false);
try {
Iterator<Entry<byte[], byte[]>> iter = data.subMap(lhs, false, rhs, false).entrySet().iterator();
doIter(scan, iter);
} finally {
scan.close();
}
// [rhs,lhs]
scan = rev.scan(lhs, true, rhs, true);
try {
Iterator<Entry<byte[], byte[]>> iter = data.subMap(lhs, true, rhs, true).entrySet().iterator();
doIter(scan, iter);
} finally {
scan.close();
}
}
public void testScanEquality() throws IOException {
byte[] val = wrapInt(10);
// [val,val]
SortedIterator<ByteBuffer> scan = reader.scan(val);
try {
Iterator<Entry<byte[], byte[]>> iter = data.subMap(val, true, val, true).entrySet().iterator();
doIter(scan, iter);
} finally {
scan.close();
}
}
public static byte[] wrapInt(int n) {
ByteBuffer buf = (ByteBuffer) ByteBuffer.allocate(4).putInt(n).flip();
return buf.array();
}
public static File[] getSoplogsToDelete() {
return new File(".").listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith("soplog") || name.endsWith("crc");
}
});
}
private void doIter(SortedIterator<ByteBuffer> scan, Iterator<Entry<byte[], byte[]>> iter) {
while (scan.hasNext() || iter.hasNext()) {
Entry<byte[], byte[]> expected = iter.next();
assertEquals(expected.getKey(), scan.next());
assertEquals(expected.getValue(), scan.value());
}
}
@Override
protected final void setUp() throws IOException {
data = new TreeMap<byte[], byte[]>(new ByteComparator());
for (int i = 0; i < 100; i++) {
data.put(wrapInt(i), wrapInt(i));
}
reader = createReader(data);
}
@Override
protected void tearDown() throws IOException {
reader.close();
for (File f : getSoplogsToDelete()) {
f.delete();
}
}
protected abstract SortedReader<ByteBuffer> createReader(NavigableMap<byte[], byte[]> data)
throws IOException;
}