package com.ctriposs.bigmap;
import static org.junit.Assert.*;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Test;
import com.ctriposs.bigmap.utils.FileUtil;
public class BigConcurrentHashMapTest {
private static String testDir = TestUtil.TEST_BASE_DIR + "bigmap/unit/big_concurrent_hashmap_test";
private BigConcurrentHashMapImpl map;
/**
* Create a map from Strings 1-5 to Strings "A"-"E".
* @throws IOException
*/
private BigConcurrentHashMapImpl map5() throws IOException {
BigConfig config = new BigConfig().setInitialCapacity(5);
BigConcurrentHashMapImpl map = new BigConcurrentHashMapImpl(testDir, "map5", config);
assertTrue(map.isEmpty());
map.put("1".getBytes(), "A".getBytes());
map.put("2".getBytes(), "B".getBytes());
map.put("3".getBytes(), "C".getBytes());
map.put("4".getBytes(), "D".getBytes());
map.put("5".getBytes(), "E".getBytes());
assertFalse(map.isEmpty());
assertEquals(5, map.size());
return map;
}
@Test
public void testClear() throws IOException {
map = map5();
map.clear();
assertEquals(map.size(), 0);
}
/**
* containsKey returns true for contained key
* @throws IOException
*/
@Test
public void testContainsKey() throws IOException {
map = map5();
assertTrue(map.containsKey("1".getBytes()));
assertFalse(map.containsKey("0".getBytes()));
}
/**
* get returns the correct element at the given key,
* or null if not present
* @throws IOException
*/
@Test
public void testGet() throws IOException {
map = map5();
String v = new String(map.get("1".getBytes()));
assertEquals("A", v);
assertNull(map.get("-1".getBytes()));
}
/**
* isEmpty is true of empty map and false for non-empty
* @throws IOException
*/
@Test
public void testIsEmpty() throws IOException {
map = new BigConcurrentHashMapImpl(testDir, "testIsEmpty");
assertTrue(map.isEmpty());
}
/**
* putIfAbsent works when the given key is not present
* @throws IOException
*/
@Test
public void testPutIfAbsent() throws IOException {
map = map5();
map.putIfAbsent("6".getBytes(), "Z".getBytes());
assertTrue(map.containsKey("6".getBytes()));
}
/**
* putIfAbsent does not add the pair if the key is already present
*/
@Test
public void testPutIfAbsent2() throws IOException {
map = map5();
assertEquals("A", new String(map.putIfAbsent("1".getBytes(), "Z".getBytes())));
}
/**
* replace fails when the given key is not present
* @throws IOException
*/
@Test
public void testReplace() throws IOException {
map = map5();
assertNull(map.replace("6".getBytes(), "Z".getBytes()));
assertFalse(map.containsKey("6".getBytes()));
}
/**
* replace succeeds if the key is already present
* @throws IOException
*/
@Test
public void testReplace2() throws IOException {
map = map5();
assertNotNull(map.replace("1".getBytes(), "Z".getBytes()));
assertEquals("Z", new String(map.get("1".getBytes())));
}
/**
* replace value fails when the given key not mapped to expected value
* @throws IOException
*/
@Test
public void testReplaceValue() throws IOException {
map = map5();
assertEquals("A", new String(map.get("1".getBytes())));
assertFalse(map.replace("1".getBytes(), "Z".getBytes(), "Z".getBytes()));
assertEquals("A", new String(map.get("1".getBytes())));
}
/**
* replace value succeeds when the given key mapped to expected value
* @throws IOException
*/
@Test
public void testReplaceValue2() throws IOException {
map = map5();
assertEquals("A", new String(map.get("1".getBytes())));
assertTrue(map.replace("1".getBytes(), "A".getBytes(), "Z".getBytes()));
assertEquals("Z", new String(map.get("1".getBytes())));
}
/**
* remove removes the correct key-value pair from the map
* @throws IOException
*/
@Test
public void testRemove() throws IOException {
map = map5();
map.remove("5".getBytes());
assertEquals(4, map.size());
assertFalse(map.containsKey("5".getBytes()));
}
/**
* remove(key,value) removes only if pair present
* @throws IOException
*/
@Test
public void testRemove2() throws IOException {
map = map5();
map.remove("5".getBytes(), "E".getBytes());
assertEquals(4, map.size());
assertFalse(map.containsKey("5".getBytes()));
map.remove("4".getBytes(), "A".getBytes());
assertEquals(4, map.size());
assertTrue(map.containsKey("4".getBytes()));
}
/**
* size returns the correct values
* @throws IOException
*/
@Test
public void testSize() throws IOException {
map = map5();
assertEquals(5, map.size());
map.clear();
map.close();
map = new BigConcurrentHashMapImpl(testDir, "testSize");
assertTrue(map.isEmpty());
}
@After
public void clear() throws IOException {
if (map != null) {
map.close();
}
FileUtil.deleteDirectory(new File(testDir));
}
/**
* Cannot create with negative capacity
* @throws IOException
*/
@Test
public void testConstructor1() throws IOException {
try {
BigConfig config = new BigConfig().setInitialCapacity(-1).setLoadFactor(0).setConcurrencyLevel(1).setPurgeIntervalInMs(1000 * 60);
map = new BigConcurrentHashMapImpl(testDir, "testConstructor1", config);
shouldThrow();
} catch(IllegalArgumentException e){}
}
/**
* Cannot create with negative concurrency level
*/
@Test
public void testConstructor2() throws IOException {
try {
BigConfig config = new BigConfig().setInitialCapacity(1).setLoadFactor(0).setConcurrencyLevel(-1).setPurgeIntervalInMs(1000 * 60);
map = new BigConcurrentHashMapImpl(testDir, "testConstructor2", config);
shouldThrow();
} catch(IllegalArgumentException e){}
}
/**
* Cannot create with only negative capacity
* @throws IOException
*/
@Test
public void testConstructor3() throws IOException {
try {
BigConfig config = new BigConfig().setInitialCapacity(-1);
map = new BigConcurrentHashMapImpl(testDir, "testConstructor3", config);
shouldThrow();
} catch(IllegalArgumentException e){}
}
/**
* containsValue(null) throws NPE
* @throws IOException
*/
@Test
public void testContainsValue_NullPointerException() throws IOException {
try {
BigConfig config = new BigConfig().setInitialCapacity(5);
map = new BigConcurrentHashMapImpl(testDir, "testContainsValue_NullPointerException", config);;
map.containsKey(null);
shouldThrow();
} catch(NullPointerException e){}
}
@Test
public void testLoop() throws IOException {
map = new BigConcurrentHashMapImpl(testDir, "testLoop");
for(int i = 0; i < 100; i++) {
map.put(String.valueOf(i).getBytes(), ("item"+i).getBytes());
}
for(int i = 0; i < 100; i++) {
String key = String.valueOf(i);
String value = new String(map.get(key.getBytes()));
assertEquals("item" + i, value);
}
}
/**
* fail with message "should throw exception"
*/
public void shouldThrow() {
fail("Should throw exception");
}
public static class CachePutterRunnable implements Runnable {
private int start;
private int count;
private BigConcurrentHashMapImpl cache;
public CachePutterRunnable(BigConcurrentHashMapImpl cache, int start, int count) {
this.cache = cache;
this.start = start;
this.count = count;
}
@Override
public void run() {
for (int i = 0; i < count; i++) {
cache.put(String.valueOf(start + i).getBytes(), ("A slightly longer string for testing " + (start + i)).getBytes());
}
}
}
@Test
public void testConcurrency() throws IOException, InterruptedException {
map = new BigConcurrentHashMapImpl(testDir, "testConcurrency");
final ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(1000);
insert(map, queue);
insert(map, queue);
long startTime = System.currentTimeMillis();
int mismatch = 0;
for(int i = 0; i < 100000; i++) {
assertEquals("A slightly longer string for testing " + i, new String(map.get(String.valueOf(i).getBytes())));
if (!("A slightly longer string for testing " + i).equals(new String(map.get(String.valueOf(i).getBytes())))){
mismatch++;
}
}
System.out.println("Mismatch :" + mismatch);
long readCompleteTime = System.currentTimeMillis();
System.out.println("Time to read: " + (readCompleteTime - startTime)
+ " ms.");
}
private void insert(BigConcurrentHashMapImpl cache, ArrayBlockingQueue<Runnable> queue) throws InterruptedException {
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(20, 20, 10, TimeUnit.SECONDS, queue);
threadPool.prestartAllCoreThreads();
long startTime = System.currentTimeMillis();
for(int i = 0; i < 20; i++) {
threadPool.execute(new CachePutterRunnable(cache, i * 5000, 5000));
}
threadPool.shutdown();
threadPool.awaitTermination(10, TimeUnit.MINUTES);
long putCompleteTime = System.currentTimeMillis();
System.out.println("Time to insert: " + (putCompleteTime - startTime) + "ms.");
}
}