-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathKVStoreSelfTest.java
More file actions
143 lines (126 loc) · 5.11 KB
/
Copy pathKVStoreSelfTest.java
File metadata and controls
143 lines (126 loc) · 5.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package com.sayandip.kvstore;
import java.util.HashSet;
import java.util.Set;
/**
* Plain-assertion self-tests (no JUnit dependency, so this builds with
* nothing but a JDK). Run with: java -cp out com.sayandip.kvstore.KVStoreSelfTest
*/
public class KVStoreSelfTest {
private static int passed = 0;
private static int failed = 0;
public static void main(String[] args) {
testBasicPutGet();
testUpdateExistingKeyDoesNotDuplicate();
testDelete();
testLRUEvictionOrder();
testAccessRefreshesRecency();
testTTLExpiryOnRead();
testHashMapResizeUnderLoad();
testHashMapHandlesCollisions();
System.out.println();
System.out.println(passed + " passed, " + failed + " failed");
if (failed > 0) System.exit(1);
}
private static void testBasicPutGet() {
KVStore<String, String> store = new KVStore<>(10, false);
store.put("x", "1");
check("basic put/get", "1".equals(store.get("x")));
check("missing key returns null", store.get("nope") == null);
}
private static void testUpdateExistingKeyDoesNotDuplicate() {
KVStore<String, String> store = new KVStore<>(10, false);
store.put("x", "1");
store.put("x", "2");
check("update overwrites value", "2".equals(store.get("x")));
check("update does not grow size", store.size() == 1);
}
private static void testDelete() {
KVStore<String, String> store = new KVStore<>(10, false);
store.put("x", "1");
store.delete("x");
check("deleted key is gone", store.get("x") == null);
check("size drops after delete", store.size() == 0);
}
private static void testLRUEvictionOrder() {
KVStore<String, String> store = new KVStore<>(3, false);
store.put("a", "1");
store.put("b", "2");
store.put("c", "3");
store.put("d", "4"); // capacity 3 exceeded, "a" is LRU -> evicted
check("LRU entry evicted", store.get("a") == null);
check("survivors still present", store.get("b") != null && store.get("c") != null && store.get("d") != null);
}
private static void testAccessRefreshesRecency() {
KVStore<String, String> store = new KVStore<>(3, false);
store.put("a", "1");
store.put("b", "2");
store.put("c", "3");
store.get("a"); // touching "a" makes it MRU, "b" becomes LRU
store.put("d", "4"); // should evict "b", not "a"
check("access refreshes recency (a survives)", store.get("a") != null);
check("access refreshes recency (b evicted)", store.get("b") == null);
}
private static void testTTLExpiryOnRead() {
KVStore<String, String> store = new KVStore<>(10, false);
store.put("temp", "val", 1); // 1 second TTL
check("not expired immediately", store.get("temp") != null);
try {
Thread.sleep(1200);
} catch (InterruptedException ignored) {
}
check("expired after TTL", store.get("temp") == null);
}
private static void testHashMapResizeUnderLoad() {
CustomHashMap<Integer, Integer> map = new CustomHashMap<>();
int n = 10_000;
for (int i = 0; i < n; i++) map.put(i, i * i);
boolean allCorrect = true;
for (int i = 0; i < n; i++) {
if (map.get(i) == null || map.get(i) != i * i) {
allCorrect = false;
break;
}
}
check("all " + n + " entries retrievable after growth", allCorrect);
check("bucket table actually grew past default 16", map.bucketCount() > 16);
check("size tracks correctly", map.size() == n);
}
private static void testHashMapHandlesCollisions() {
// A hashCode that forces every key into the same bucket, no
// matter how many times the table resizes -- proves chaining
// actually works and isn't silently dropping entries.
class CollidingKey {
final int id;
CollidingKey(int id) { this.id = id; }
@Override public int hashCode() { return 42; }
@Override public boolean equals(Object o) {
return o instanceof CollidingKey && ((CollidingKey) o).id == this.id;
}
}
CustomHashMap<CollidingKey, String> map = new CustomHashMap<>();
Set<CollidingKey> keys = new HashSet<>();
for (int i = 0; i < 200; i++) {
CollidingKey k = new CollidingKey(i);
keys.add(k);
map.put(k, "v" + i);
}
boolean allCorrect = true;
for (CollidingKey k : keys) {
String expected = "v" + k.id;
if (!expected.equals(map.get(k))) {
allCorrect = false;
break;
}
}
check("200 same-bucket keys all resolve correctly via chaining", allCorrect);
}
private static void check(String label, boolean condition) {
if (condition) {
passed++;
System.out.println("PASS - " + label);
} else {
failed++;
System.out.println("FAIL - " + label);
}
}
}