|
@@ -0,0 +1,271 @@
|
|
|
|
|
+/*
|
|
|
|
|
+ * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
|
|
+ * @copyright Ranides Atterwim
|
|
|
|
|
+ * @license WTFPL
|
|
|
|
|
+ * @url http://ranides.net/projects/assira
|
|
|
|
|
+ */
|
|
|
|
|
+package net.ranides.assira.collection.maps;
|
|
|
|
|
+
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
|
+import java.util.Collection;
|
|
|
|
|
+import java.util.HashSet;
|
|
|
|
|
+import java.util.Iterator;
|
|
|
|
|
+import java.util.LinkedHashMap;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+import java.util.Map;
|
|
|
|
|
+import java.util.Random;
|
|
|
|
|
+import java.util.Set;
|
|
|
|
|
+import net.ranides.assira.collection.CollectionUtils;
|
|
|
|
|
+import net.ranides.assira.system.RuntimeUtils;
|
|
|
|
|
+import net.ranides.assira.text.StrBuilder;
|
|
|
|
|
+import org.junit.Test;
|
|
|
|
|
+import static org.junit.Assert.*;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ *
|
|
|
|
|
+ * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
|
|
+ */
|
|
|
|
|
+public class CacheTest {
|
|
|
|
|
+
|
|
|
|
|
+ public Cache<Integer, String> createPMap() {
|
|
|
|
|
+ // tworzymy cache, który chroni wszystkie swoje elementy przed GC
|
|
|
|
|
+ Cache<Integer, String> cache = new Cache<>(32);
|
|
|
|
|
+ for(int i=0; i<48; i++) {
|
|
|
|
|
+ cache.put(i, "BNGV-"+i);
|
|
|
|
|
+ }
|
|
|
|
|
+ return cache;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ public void testContainsKey() {
|
|
|
|
|
+ Cache<Integer, String> cache = createPMap();
|
|
|
|
|
+
|
|
|
|
|
+ assertFalse( cache.containsKey(5) );
|
|
|
|
|
+ assertFalse( cache.containsKey(12) );
|
|
|
|
|
+ assertFalse( cache.containsKey(15) );
|
|
|
|
|
+
|
|
|
|
|
+ assertTrue( cache.containsKey(16) );
|
|
|
|
|
+ assertTrue( cache.containsKey(30) );
|
|
|
|
|
+ assertTrue( cache.containsKey(47) );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ public void testGet() {
|
|
|
|
|
+ Cache<Integer, String> cache = createPMap();
|
|
|
|
|
+ assertNull( cache.get(5) );
|
|
|
|
|
+ assertNull( cache.get(12) );
|
|
|
|
|
+ assertNull( cache.get(15) );
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals("BNGV-16", cache.get(16));
|
|
|
|
|
+ assertEquals("BNGV-30", cache.get(30));
|
|
|
|
|
+ assertEquals("BNGV-47", cache.get(47));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ public void testRemove() {
|
|
|
|
|
+ Cache<Integer, String> cache = createPMap();
|
|
|
|
|
+ assertTrue( cache.containsKey(32) );
|
|
|
|
|
+ cache.remove(32);
|
|
|
|
|
+ assertFalse( cache.containsKey(32) );
|
|
|
|
|
+ cache.remove(3);
|
|
|
|
|
+ cache.remove(300);
|
|
|
|
|
+ assertFalse( cache.containsKey(3) );
|
|
|
|
|
+ assertFalse( cache.containsKey(300) );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ public void testClear() {
|
|
|
|
|
+ Cache<Integer, String> cache = createPMap();
|
|
|
|
|
+ cache.clear();
|
|
|
|
|
+ assertEquals(0, cache.size());
|
|
|
|
|
+ assertFalse( cache.containsKey(16) );
|
|
|
|
|
+ assertFalse( cache.containsKey(30) );
|
|
|
|
|
+ assertFalse( cache.containsKey(47) );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ public void testSize() {
|
|
|
|
|
+ Cache<Integer, String> cache = createPMap();
|
|
|
|
|
+ assertEquals(32, cache.size());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ public void testEntrySet() {
|
|
|
|
|
+ Cache<Integer, String> cache = createPMap();
|
|
|
|
|
+
|
|
|
|
|
+ Set<Map.Entry<Integer,String>> entries = cache.entrySet();
|
|
|
|
|
+ assertEquals(cache.size(), entries.size());
|
|
|
|
|
+
|
|
|
|
|
+ Map<Integer, String> copy1 = new java.util.HashMap<>();
|
|
|
|
|
+ for(Map.Entry<Integer,String> entry : entries) {
|
|
|
|
|
+ copy1.put(entry.getKey(), entry.getValue());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals(cache, copy1);
|
|
|
|
|
+
|
|
|
|
|
+ Map<Integer, String> copy2 = new java.util.HashMap<>();
|
|
|
|
|
+ Iterator<Map.Entry<Integer,String>> iterator = entries.iterator();
|
|
|
|
|
+ int i = 0;
|
|
|
|
|
+ while(iterator.hasNext()) {
|
|
|
|
|
+ Map.Entry<Integer,String> entry = iterator.next();
|
|
|
|
|
+ copy2.put(entry.getKey(), entry.getValue());
|
|
|
|
|
+ if( i++%2 == 0 ) {
|
|
|
|
|
+ iterator.remove();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals(copy1.size() / 2, cache.size());
|
|
|
|
|
+ assertEquals(copy1, copy2);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ public void testKeySet() {
|
|
|
|
|
+ Cache<Integer, String> cache = createPMap();
|
|
|
|
|
+
|
|
|
|
|
+ Set<Integer> keys = cache.keySet();
|
|
|
|
|
+ assertEquals(cache.size(), keys.size());
|
|
|
|
|
+
|
|
|
|
|
+ Set<Integer> keys1 = new HashSet<>(keys);
|
|
|
|
|
+ Map<Integer, String> copy1 = new java.util.HashMap<>();
|
|
|
|
|
+ for(Integer key : keys1) {
|
|
|
|
|
+ copy1.put(key, cache.get(key));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals(cache, copy1);
|
|
|
|
|
+
|
|
|
|
|
+ Iterator<Integer> iterator = keys.iterator();
|
|
|
|
|
+ int i = 0;
|
|
|
|
|
+ while(iterator.hasNext()) {
|
|
|
|
|
+ iterator.next();
|
|
|
|
|
+ if( i++%2 == 0 ) {
|
|
|
|
|
+ iterator.remove();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals(copy1.size() / 2, cache.size());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ public void testValues() {
|
|
|
|
|
+ Cache<Integer, String> cache = createPMap();
|
|
|
|
|
+
|
|
|
|
|
+ Collection<String> values = cache.values();
|
|
|
|
|
+ assertEquals(cache.size(), values.size());
|
|
|
|
|
+
|
|
|
|
|
+ Map<Integer, String> copy1 = new LinkedHashMap<>(cache);
|
|
|
|
|
+ List<String> values1 = new ArrayList<>(values);
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals(cache.size(), copy1.size());
|
|
|
|
|
+
|
|
|
|
|
+ // kolejność nie musi być zachowana
|
|
|
|
|
+ assertTrue( CollectionUtils.equivalent(copy1.values(), values) );
|
|
|
|
|
+ // kolejność nie musi być zachowana
|
|
|
|
|
+ assertTrue( CollectionUtils.equivalent(values1, values) );
|
|
|
|
|
+
|
|
|
|
|
+ Iterator<String> iterator = values.iterator();
|
|
|
|
|
+ int i = 0;
|
|
|
|
|
+ while(iterator.hasNext()) {
|
|
|
|
|
+ iterator.next();
|
|
|
|
|
+ if( i++%2 == 0 ) {
|
|
|
|
|
+ iterator.remove();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals(copy1.size() / 2, cache.size());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ public void testRelease() {
|
|
|
|
|
+ // we created that cache to check as much things as possible in one method
|
|
|
|
|
+ // we don't want to run "forceGC" more than once, because its very costly
|
|
|
|
|
+ Cache<Integer, String> cache0 = new Cache<>(32);
|
|
|
|
|
+ Random rand = new Random(777);
|
|
|
|
|
+
|
|
|
|
|
+ Cache<Integer, String> cache = new Cache<>(128, 16);
|
|
|
|
|
+ for(int i=0; i<256; i++) {
|
|
|
|
|
+ cache0.put(i, text(rand, 32));
|
|
|
|
|
+ cache.put(i, text(rand, 32));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals(32, cache0.hold());
|
|
|
|
|
+
|
|
|
|
|
+ // we should have only last 128 elements
|
|
|
|
|
+ assertEquals(128, cache.size());
|
|
|
|
|
+ // we should not hold anything (we didn't make any access till now)
|
|
|
|
|
+ assertEquals(0, cache.holdsize());
|
|
|
|
|
+
|
|
|
|
|
+ assertFalse( cache.containsKey(12) );
|
|
|
|
|
+ assertTrue( cache.containsKey(160) );
|
|
|
|
|
+ // we should hold 1 accessed element (i.e. 160)
|
|
|
|
|
+ assertEquals(1, cache.holdsize());
|
|
|
|
|
+
|
|
|
|
|
+ // we should hold as many as possible
|
|
|
|
|
+ assertEquals(16, cache.hold());
|
|
|
|
|
+ assertEquals(true, cache.hold(255)); // it will create "duplicate in holdlist"
|
|
|
|
|
+ assertEquals(false, cache.hold(17));
|
|
|
|
|
+ assertEquals(16, cache.holdsize());
|
|
|
|
|
+ assertEquals(128, cache.size());
|
|
|
|
|
+
|
|
|
|
|
+ // 512 KB is reasonable minimum of unreachable memory
|
|
|
|
|
+ // all soft references should stay
|
|
|
|
|
+ assertTrue( RuntimeUtils.performGC() > 512*1024);
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals(128, cache.size());
|
|
|
|
|
+ assertEquals(16, cache.holdsize());
|
|
|
|
|
+ assertTrue(cache.containsKey(160)); // it will create "duplicate in holdlist"
|
|
|
|
|
+ assertTrue(cache.containsKey(252)); // it will create "duplicate in holdlist"
|
|
|
|
|
+ assertEquals(16, cache.holdsize());
|
|
|
|
|
+
|
|
|
|
|
+ // 512 KB is reasonable minimum of unreachable memory
|
|
|
|
|
+ // all soft references should go away
|
|
|
|
|
+ assertTrue( RuntimeUtils.forceGC() > 512*1024 );
|
|
|
|
|
+
|
|
|
|
|
+ // test clean-up code in "get".
|
|
|
|
|
+ // we can't invoke almost any method after GC, because it will run
|
|
|
|
|
+ // clean-up code in #relase method
|
|
|
|
|
+ // we can't invoke #contains on existing key, because it will create
|
|
|
|
|
+ // "duplicate in holdlist"
|
|
|
|
|
+ assertFalse(cache.containsKey(161));
|
|
|
|
|
+ assertFalse(cache.containsKey(240));
|
|
|
|
|
+
|
|
|
|
|
+ // we have 3 duplicates in holdlist
|
|
|
|
|
+ assertEquals(13, cache.size());
|
|
|
|
|
+ assertEquals(16, cache.holdsize());
|
|
|
|
|
+
|
|
|
|
|
+ assertTrue(cache.containsKey(160));
|
|
|
|
|
+ assertTrue(cache.containsKey(252));
|
|
|
|
|
+
|
|
|
|
|
+ // we dont have any duplicates
|
|
|
|
|
+ assertEquals(32, cache0.size());
|
|
|
|
|
+ assertEquals(32, cache0.holdsize());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ public void testPass() {
|
|
|
|
|
+ Cache<Integer, String> cache = new Cache<>();
|
|
|
|
|
+ assertSame("A", cache.pass(17, "A"));
|
|
|
|
|
+ assertSame("B", cache.pass(17, "B"));
|
|
|
|
|
+ assertSame("C", cache.pass(17, "C"));
|
|
|
|
|
+ assertEquals(1, cache.size());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ public void testConstruct() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ Cache<Integer, String> cache3 = new Cache<>(32,48);
|
|
|
|
|
+ fail("IllegalArgumentException expected");
|
|
|
|
|
+ } catch(IllegalArgumentException cause) {
|
|
|
|
|
+ assertTrue(true);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static String text(Random rand, int size) {
|
|
|
|
|
+ StrBuilder sb = new StrBuilder(size);
|
|
|
|
|
+ for(int i=0; i<size; i++) {
|
|
|
|
|
+ sb.append( (char)(' '+rand.nextInt(128)) );
|
|
|
|
|
+ }
|
|
|
|
|
+ return sb.toString();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+}
|