blob: 8bfc820bdae71d2e2042a1d8a58567f3883684f9 [file] [log] [blame]
Ray Milkey2eb91672018-04-24 10:07:52 -07001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.store.primitives;
18
19import org.hamcrest.Description;
20import org.hamcrest.TypeSafeMatcher;
21import org.junit.Before;
22import org.junit.Test;
23import org.onosproject.store.service.ConsistentMapException;
24import org.onosproject.store.service.MapEvent;
25import org.onosproject.store.service.MapEventListener;
26import org.onosproject.store.service.Versioned;
27
28import java.util.LinkedList;
29import java.util.Map;
30import java.util.Objects;
31import java.util.Set;
32import java.util.concurrent.CompletableFuture;
33import java.util.concurrent.Executor;
34import java.util.stream.Collectors;
35
36import static org.hamcrest.MatcherAssert.assertThat;
37import static org.hamcrest.Matchers.hasItems;
38import static org.hamcrest.Matchers.hasSize;
39import static org.hamcrest.Matchers.is;
40import static org.hamcrest.Matchers.notNullValue;
41import static org.onosproject.store.primitives.TestingCompletableFutures.ErrorState.NONE;
42
43public class DefaultConsistentTreeMapTest {
44
45 private static final String MIN_KEY = "0";
46 private static final String KEY1 = "A";
47 private static final String VALUE1 = "A";
48 private static final String KEY2 = "B";
49 private static final String VALUE2 = "B";
50 private static final String KEY3 = "C";
51 private static final String VALUE3 = "C";
52 private static final String KEY4 = "D";
53 private static final String VALUE4 = "D";
54 private static final String KEY5 = "E";
55 private static final String VALUE5 = "E";
56 private static final String MAX_KEY = "Z";
57 private static final String NO_SUCH_VALUE = "BAD VALUE";
58 private static final String NO_SUCH_KEY = "BAD KEY";
59 private static final String DEFAULT_VALUE = "DEFAULT";
60
61 private DefaultConsistentTreeMap<String> treeMap;
62 private TestAsyncConsistentTreeMap<String> asyncMap;
63
64 private static class TestAsyncConsistentTreeMap<V> extends AsyncConsistentTreeMapAdapter<V> {
65 LinkedList<MapEventListener<String, V>> listeners = new LinkedList<>();
66
67 @Override
68 public CompletableFuture<Void> addListener(MapEventListener<String, V> listener,
69 Executor executor) {
70 listeners.add(listener);
71 return CompletableFuture.completedFuture(null);
72 }
73
74 @Override
75 public CompletableFuture<Void> removeListener(MapEventListener<String, V> listener) {
76 listeners.remove(listener);
77 return CompletableFuture.completedFuture(null);
78 }
79 }
80
81 class Listener implements MapEventListener<String, String> {
82 @Override
83 public void event(MapEvent<String, String> event) {
84 // Nothing to do here
85 }
86 }
87
88 private DefaultConsistentTreeMap<String> createMap() {
89 asyncMap = new TestAsyncConsistentTreeMap<>();
90 DefaultConsistentTreeMap<String> map = new DefaultConsistentTreeMap<>(asyncMap, 1000L);
91 assertThat(map, notNullValue());
92 assertThat(map.isEmpty(), is(true));
93 map.putIfAbsent(KEY1, VALUE1);
94 map.putIfAbsent(KEY2, VALUE2);
95 map.putIfAbsent(KEY3, VALUE3);
96 return map;
97 }
98
99 static class VersionedMatcher extends TypeSafeMatcher<Versioned<String>> {
100 String expectedValue;
101
102 VersionedMatcher(String expectedValue) {
103 this.expectedValue = expectedValue;
104 }
105
106 @Override
107 public boolean matchesSafely(Versioned<String> value) {
108 return expectedValue.equals(value.value());
109 }
110
111 @Override
112 public void describeTo(Description description) {
113 description.appendText("<Versioned{value=" + expectedValue + ",...");
114 }
115 }
116
117 private static VersionedMatcher matchesVersioned(String expectedValue) {
118 return new VersionedMatcher(expectedValue);
119 }
120
121 @Before
122 public void setUpMap() {
123 treeMap = createMap();
124 }
125
126 @Test
127 public void testKeys() {
128 assertThat(treeMap.size(), is(3));
129 assertThat(treeMap.navigableKeySet(), hasSize(3));
130
131 assertThat(treeMap.firstKey(), is(VALUE1));
132 assertThat(treeMap.lastKey(), is(VALUE3));
133 assertThat(treeMap.lowerKey(KEY2), is(VALUE1));
134 assertThat(treeMap.higherKey(KEY2), is(VALUE3));
135 assertThat(treeMap.floorKey(MAX_KEY), is(VALUE3));
136 assertThat(treeMap.ceilingKey(MIN_KEY), is(VALUE1));
137
138 assertThat(treeMap.containsKey(KEY2), is(true));
139 assertThat(treeMap.containsKey(MAX_KEY), is(false));
140 }
141
142 private void checkEntry(Map.Entry<String, Versioned<String>> entry,
143 String expectedKey,
144 String expectedValue) {
145 assertThat(entry.getKey(), is(expectedKey));
146 assertThat(entry.getValue(), matchesVersioned(expectedValue));
147 }
148
149 @Test
150 public void testEntries() {
151 assertThat(treeMap.size(), is(3));
152
153 checkEntry(treeMap.firstEntry(), KEY1, VALUE1);
154 checkEntry(treeMap.lastEntry(), KEY3, VALUE3);
155 checkEntry(treeMap.lowerEntry(KEY2), KEY1, VALUE1);
156 checkEntry(treeMap.higherEntry(KEY2), KEY3, VALUE3);
157 checkEntry(treeMap.floorEntry(MAX_KEY), KEY3, VALUE3);
158 checkEntry(treeMap.ceilingEntry(MIN_KEY), KEY1, VALUE1);
159
160 checkEntry(treeMap.pollFirstEntry(), KEY1, VALUE1);
161 assertThat(treeMap.size(), is(2));
162
163 checkEntry(treeMap.pollLastEntry(), KEY3, VALUE3);
164 assertThat(treeMap.size(), is(1));
165 }
166
167 @Test
168 public void testGets() {
169 assertThat(treeMap.get(KEY2), matchesVersioned(VALUE2));
170 assertThat(treeMap.containsValue(VALUE3), is(true));
171 assertThat(treeMap.getOrDefault(KEY3, DEFAULT_VALUE), matchesVersioned(VALUE3));
172 assertThat(treeMap.getOrDefault(NO_SUCH_KEY, DEFAULT_VALUE), matchesVersioned(DEFAULT_VALUE));
173 assertThat(treeMap.compute(KEY4, (k, v) -> v == null ? VALUE4 : ""), matchesVersioned(VALUE4));
174 assertThat(treeMap.computeIf(KEY4, Objects::isNull, (k, v) -> NO_SUCH_VALUE), matchesVersioned(VALUE4));
175 assertThat(treeMap.computeIfPresent(KEY4, (k, v) -> NO_SUCH_VALUE), matchesVersioned(NO_SUCH_VALUE));
176 assertThat(treeMap.computeIfAbsent(KEY2, (v) -> NO_SUCH_VALUE), matchesVersioned(VALUE2));
177 treeMap.put(KEY5, VALUE5);
178 assertThat(treeMap.putAndGet(KEY5, VALUE1), matchesVersioned(VALUE5));
179 assertThat(treeMap.get(KEY5), matchesVersioned(VALUE1));
180 }
181
182 @Test
183 public void testSets() {
184 Set<String> keys = treeMap.keySet();
185 assertThat(keys, hasSize(3));
186 assertThat(keys, hasItems(KEY1, KEY2, KEY3));
187
188 Set<String> values = treeMap.values().stream().map(Versioned::value).collect(Collectors.toSet());
189 assertThat(values, hasSize(3));
190 assertThat(values, hasItems(VALUE1, VALUE2, VALUE3));
191
192 Set<String> valuesFromEntries = treeMap.entrySet().stream()
193 .map(entry -> entry.getValue().value()).collect(Collectors.toSet());
194 assertThat(valuesFromEntries, hasSize(3));
195 assertThat(valuesFromEntries, hasItems(VALUE1, VALUE2, VALUE3));
196
197 Set<String> keysFromEntries = treeMap.entrySet().stream()
198 .map(Map.Entry::getKey).collect(Collectors.toSet());
199 assertThat(keysFromEntries, hasSize(3));
200 assertThat(keysFromEntries, hasItems(KEY1, KEY2, KEY3));
201 }
202
203 @Test
204 public void testRemoves() {
205 treeMap.remove(KEY1);
206 assertThat(treeMap.size(), is(2));
207
208 treeMap.remove(KEY2, 1L);
209 assertThat(treeMap.size(), is(1));
210
211 treeMap.remove(KEY3, VALUE3);
212 assertThat(treeMap.size(), is(0));
213 }
214
215 @Test
216 public void testClear() {
217 treeMap.clear();
218 assertThat(treeMap.size(), is(0));
219 }
220
221 @Test
222 public void testReplaces() {
223 treeMap.replace(KEY1, VALUE2);
224 assertThat(treeMap.get(KEY1), matchesVersioned(VALUE2));
225
226 treeMap.replace(KEY2, 1L, VALUE1);
227 assertThat(treeMap.get(KEY2), matchesVersioned(VALUE1));
228
229 treeMap.replace(KEY3, VALUE3, VALUE5);
230 assertThat(treeMap.get(KEY3), matchesVersioned(VALUE5));
231 }
232
233 @Test
234 public void testJavaMap() {
235 Map<String, String> javaMap = treeMap.asJavaMap();
236 assertThat(javaMap.entrySet(), hasSize(3));
237 assertThat(javaMap.values(), hasItems(VALUE1, VALUE2, VALUE3));
238 assertThat(javaMap.keySet(), hasItems(KEY1, KEY2, KEY3));
239 }
240
241 @Test
242 public void testSubMap() {
243 treeMap.putIfAbsent(KEY4, VALUE4);
244 treeMap.putIfAbsent(KEY5, VALUE5);
245
246 Map<String, String> subMap = treeMap.subMap(KEY2, KEY4, true, true);
247 assertThat(subMap.entrySet(), hasSize(3));
248 assertThat(subMap.values(), hasItems(VALUE2, VALUE3, VALUE4));
249 assertThat(subMap.keySet(), hasItems(KEY2, KEY3, KEY4));
250 }
251
252 @Test
253 public void testListeners() {
254 Listener listener1 = new Listener();
255 Listener listener2 = new Listener();
256
257 assertThat(asyncMap.listeners, hasSize(0));
258 treeMap.addListener(listener1);
259 assertThat(asyncMap.listeners, hasSize(1));
260 treeMap.addListener(listener2);
261 assertThat(asyncMap.listeners, hasSize(2));
262
263 treeMap.removeListener(listener1);
264 treeMap.removeListener(listener2);
265 assertThat(asyncMap.listeners, hasSize(0));
266 }
267
268 class ConsistentTreeMapWithError<K> extends AsyncConsistentTreeMapAdapter<K> {
269
270 TestingCompletableFutures.ErrorState errorState = NONE;
271
272 void setErrorState(TestingCompletableFutures.ErrorState errorState) {
273 this.errorState = errorState;
274 }
275
276 ConsistentTreeMapWithError() {
277 super();
278 }
279
280 @Override
281 public CompletableFuture<String> lowerKey(String key) {
282 return TestingCompletableFutures.createStringFuture(errorState);
283 }
284 }
285
286 @Test(expected = ConsistentMapException.Timeout.class)
287 public void testTimeout() {
288 ConsistentTreeMapWithError<String> consistentMap =
289 new ConsistentTreeMapWithError<>();
290 consistentMap.setErrorState(TestingCompletableFutures.ErrorState.TIMEOUT_EXCEPTION);
291 DefaultConsistentTreeMap<String> map =
292 new DefaultConsistentTreeMap<>(consistentMap, 1000);
293
294 map.lowerKey(KEY1);
295 }
296
297
298 @Test(expected = ConsistentMapException.Interrupted.class)
299 public void testInterrupted() {
300 ConsistentTreeMapWithError<String> consistentMap =
301 new ConsistentTreeMapWithError<>();
302 consistentMap.setErrorState(TestingCompletableFutures.ErrorState.INTERRUPTED_EXCEPTION);
303 DefaultConsistentTreeMap<String> map =
304 new DefaultConsistentTreeMap<>(consistentMap, 1000);
305
306 map.lowerKey(KEY1);
307 }
308
309 @Test(expected = ConsistentMapException.class)
310 public void testExecutionError() {
311 ConsistentTreeMapWithError<String> consistentMap =
312 new ConsistentTreeMapWithError<>();
313 consistentMap.setErrorState(TestingCompletableFutures.ErrorState.EXECUTION_EXCEPTION);
314 DefaultConsistentTreeMap<String> map =
315 new DefaultConsistentTreeMap<>(consistentMap, 1000);
316
317 map.lowerKey(KEY1);
318 }
319
320}