blob: 12df7125111a267379ea44471032f33d1ebedc57 [file] [log] [blame]
Ray Milkey9011dfe2017-03-17 10:33:25 -07001/*
Brian O'Connorce2a03d2017-08-03 19:21:03 -07002 * Copyright 2017-present Open Networking Foundation
Ray Milkey9011dfe2017-03-17 10:33:25 -07003 *
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 */
16package org.onosproject.store.primitives;
17
18import java.util.ArrayList;
19import java.util.Collection;
20import java.util.HashMap;
21import java.util.List;
22import java.util.Map;
23import java.util.Set;
24import java.util.concurrent.CompletableFuture;
25import java.util.concurrent.Executor;
26import java.util.function.BiFunction;
27import java.util.function.Consumer;
28import java.util.function.Predicate;
29import java.util.stream.Collectors;
30
31import org.junit.Test;
32import org.onosproject.store.service.AsyncConsistentMapAdapter;
33import org.onosproject.store.service.ConsistentMap;
34import org.onosproject.store.service.DistributedPrimitive;
35import org.onosproject.store.service.MapEvent;
36import org.onosproject.store.service.MapEventListener;
37import org.onosproject.store.service.Versioned;
38
39import static org.hamcrest.CoreMatchers.hasItem;
40import static org.hamcrest.CoreMatchers.not;
41import static org.hamcrest.CoreMatchers.nullValue;
42import static org.hamcrest.MatcherAssert.assertThat;
43import static org.hamcrest.Matchers.containsString;
44import static org.hamcrest.Matchers.hasSize;
45import static org.hamcrest.Matchers.is;
46
47/**
48 * Tests for DefaultConsistentMap.
49 */
50public class DefaultConsistentMapTest {
51
52 private static final int DEFAULT_CREATION_TIME = 0;
53 private static final int DEFAULT_VERSION = 0;
54
55
56 public class AsyncConsistentMapMock<K, V> extends AsyncConsistentMapAdapter<K, V> {
57 private final List<MapEventListener<K, V>> listeners;
58 Collection<Consumer<Status>> statusChangeListeners = new ArrayList<>();
59 private final Map<K, V> baseMap;
60
61 Versioned<V> makeVersioned(V v) {
62 return new Versioned<>(v, DEFAULT_VERSION, DEFAULT_CREATION_TIME);
63 }
64
65 AsyncConsistentMapMock(Map<K, V> newBaseMap) {
66 baseMap = newBaseMap;
67 listeners = new ArrayList<>();
68 }
69
70 public CompletableFuture<Integer> size() {
71 return CompletableFuture.completedFuture(baseMap.size());
72 }
73
74 @Override
75 public CompletableFuture<Boolean> containsKey(K key) {
76 return CompletableFuture.completedFuture(baseMap.containsKey(key));
77 }
78
79 @Override
80 public CompletableFuture<Boolean> containsValue(V value) {
81 return CompletableFuture.completedFuture(baseMap.containsValue(value));
82 }
83
84 @Override
85 public CompletableFuture<Versioned<V>> get(K key) {
86 return CompletableFuture.completedFuture(makeVersioned(baseMap.get(key)));
87 }
88
89 @Override
90 public CompletableFuture<Versioned<V>>
91 computeIf(K key, Predicate<? super V> condition,
92 BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
93
94 V value = baseMap.get(key);
95
96 if (condition.test(value)) {
97 value = baseMap.compute(key, remappingFunction);
98 }
99 return CompletableFuture.completedFuture(makeVersioned(value));
100 }
101
102 @Override
103 public CompletableFuture<Versioned<V>> put(K key, V value) {
104 return CompletableFuture.completedFuture(makeVersioned(baseMap.put(key, value)));
105 }
106
107 @Override
108 public CompletableFuture<Versioned<V>> putAndGet(K key, V value) {
109 return CompletableFuture.completedFuture(makeVersioned(baseMap.put(key, value)));
110 }
111
112 @Override
113 public CompletableFuture<Versioned<V>> remove(K key) {
114 return CompletableFuture.completedFuture(makeVersioned(baseMap.remove(key)));
115 }
116
117 @Override
118 public CompletableFuture<Void> clear() {
119 baseMap.clear();
120 return CompletableFuture.allOf();
121 }
122
123 @Override
124 public CompletableFuture<Set<K>> keySet() {
125 return CompletableFuture.completedFuture(baseMap.keySet());
126 }
127
128 @Override
129 public CompletableFuture<Collection<Versioned<V>>> values() {
130 Set<Versioned<V>> valuesAsVersionedCollection =
131 baseMap.values().stream().map(this::makeVersioned)
132 .collect(Collectors.toSet());
133 return CompletableFuture.completedFuture(valuesAsVersionedCollection);
134 }
135
136 @Override
137 public CompletableFuture<Set<Map.Entry<K, Versioned<V>>>> entrySet() {
138 Map<K, Versioned<V>> valuesAsVersionedMap = new HashMap<>();
139 baseMap.entrySet()
140 .forEach(e -> valuesAsVersionedMap.put(e.getKey(),
141 makeVersioned(e.getValue())));
142 return CompletableFuture.completedFuture(valuesAsVersionedMap.entrySet());
143 }
144
145 @Override
146 public CompletableFuture<Versioned<V>> putIfAbsent(K key, V value) {
147 return CompletableFuture.completedFuture(makeVersioned(baseMap.putIfAbsent(key, value)));
148 }
149
150 @Override
151 public CompletableFuture<Boolean> remove(K key, V value) {
152 return CompletableFuture.completedFuture(baseMap.remove(key, value));
153 }
154
155 @Override
156 public CompletableFuture<Boolean> remove(K key, long version) {
157 Object value = baseMap.remove(key);
158 return CompletableFuture.completedFuture(value != null);
159 }
160
161 @Override
162 public CompletableFuture<Versioned<V>> replace(K key, V value) {
163 return CompletableFuture.completedFuture(makeVersioned(baseMap.replace(key, value)));
164 }
165
166 @Override
167 public CompletableFuture<Boolean> replace(K key, V oldValue, V newValue) {
168 return CompletableFuture.completedFuture(baseMap.replace(key, oldValue, newValue));
169 }
170
171 @Override
172 public CompletableFuture<Boolean> replace(K key, long oldVersion, V newValue) {
173 return CompletableFuture.completedFuture(baseMap.replace(key, newValue) != null);
174 }
175
176 @Override
177 public CompletableFuture<Void> addListener(MapEventListener<K, V> listener, Executor e) {
178 listeners.add(listener);
179 return CompletableFuture.allOf();
180 }
181
182 @Override
183 public CompletableFuture<Void> removeListener(MapEventListener<K, V> listener) {
184 listeners.remove(listener);
185 return CompletableFuture.allOf();
186 }
187
188 @Override
189 public void addStatusChangeListener(Consumer<Status> listener) {
190 statusChangeListeners.add(listener);
191 }
192
193 @Override
194 public void removeStatusChangeListener(Consumer<Status> listener) {
195 statusChangeListeners.remove(listener);
196 }
197
198 @Override
199 public Collection<Consumer<Status>> statusChangeListeners() {
200 return statusChangeListeners;
201 }
202 }
203
204 private static final String KEY1 = "AAA";
205 private static final String VALUE1 = "111";
206 private static final String KEY2 = "BBB";
207 private static final String VALUE2 = "222";
208 private static final String KEY3 = "CCC";
209 private static final String VALUE3 = "333";
210 private static final String KEY4 = "DDD";
211 private static final String VALUE4 = "444";
212
213 private int computeFunctionCalls = 0;
214 private String computeFunction(String s) {
215 computeFunctionCalls++;
216 if (KEY4.equals(s)) {
217 return VALUE4;
218 }
219 return "";
220 }
221
222 class Listener implements MapEventListener<String, String> {
223 final int id;
224
225 Listener(int newId) {
226 id = newId;
227 }
228
229 @Override
230 public void event(MapEvent<String, String> event) {
231 // Nothing to do here
232 }
233 }
234
235 /**
236 * Tests the behavior of public APIs of the default consistent map
237 * implmentation.
238 */
239 @Test
240 public void testBehavior() {
241
242 Map<String, String> baseMap = new HashMap<>();
243 AsyncConsistentMapMock<String, String> asyncMap =
244 new AsyncConsistentMapMock<>(baseMap);
245 ConsistentMap<String, String> newMap =
246 new DefaultConsistentMap<>(asyncMap, 11);
247 assertThat(newMap.size(), is(0));
248 assertThat(newMap.isEmpty(), is(true));
249
250 newMap.put(KEY1, VALUE1);
251 assertThat(newMap.size(), is(1));
252 assertThat(newMap.get(KEY1).value(), is(VALUE1));
253 assertThat(newMap.containsKey(KEY1), is(true));
254 assertThat(newMap.containsKey(VALUE1), is(false));
255 assertThat(newMap.containsValue(VALUE1), is(true));
256 assertThat(newMap.containsValue(KEY1), is(false));
257 assertThat(newMap.keySet(), hasSize(1));
258 assertThat(newMap.keySet(), hasItem(KEY1));
259 assertThat(newMap.values(), hasSize(1));
260 assertThat(newMap.values(), hasItem(new Versioned<>(VALUE1, 0, 0)));
261 assertThat(newMap.entrySet(), hasSize(1));
262 Map.Entry<String, Versioned<String>> entry = newMap.entrySet().iterator().next();
263 assertThat(entry.getKey(), is(KEY1));
264 assertThat(entry.getValue().value(), is(VALUE1));
265
266 newMap.putIfAbsent(KEY2, VALUE2);
267 assertThat(newMap.entrySet(), hasSize(2));
268 assertThat(newMap.get(KEY2).value(), is(VALUE2));
269 newMap.putIfAbsent(KEY2, VALUE1);
270 assertThat(newMap.entrySet(), hasSize(2));
271 assertThat(newMap.get(KEY2).value(), is(VALUE2));
272
273 newMap.putAndGet(KEY3, VALUE3);
274 assertThat(newMap.entrySet(), hasSize(3));
275 assertThat(newMap.get(KEY3).value(), is(VALUE3));
276
277 newMap.putIfAbsent(KEY3, VALUE1);
278 assertThat(newMap.entrySet(), hasSize(3));
279 assertThat(newMap.get(KEY3).value(), is(VALUE3));
280
281 assertThat(newMap.computeIfAbsent(KEY4, this::computeFunction).value(), is(VALUE4));
282 assertThat(computeFunctionCalls, is(1));
283 assertThat(newMap.entrySet(), hasSize(4));
284 assertThat(newMap.computeIfAbsent(KEY4, this::computeFunction).value(), is(VALUE4));
285 assertThat(computeFunctionCalls, is(1));
286
287 Map javaMap = newMap.asJavaMap();
288 assertThat(javaMap.size(), is(newMap.size()));
289 assertThat(javaMap.get(KEY1), is(VALUE1));
290
291 assertThat(newMap.toString(), containsString(KEY4 + "=" + VALUE4));
292
293 assertThat(newMap.remove(KEY4).value(), is(VALUE4));
294 assertThat(newMap.entrySet(), hasSize(3));
295 assertThat(newMap.remove(KEY4).value(), nullValue());
296 assertThat(newMap.entrySet(), hasSize(3));
297
298 assertThat(newMap.remove(KEY3, DEFAULT_VERSION), is(true));
299 assertThat(newMap.entrySet(), hasSize(2));
300 assertThat(newMap.remove(KEY3, DEFAULT_VERSION), is(false));
301 assertThat(newMap.entrySet(), hasSize(2));
302
303 assertThat(newMap.remove(KEY2, VALUE2), is(true));
304 assertThat(newMap.entrySet(), hasSize(1));
305 assertThat(newMap.remove(KEY2, VALUE2), is(false));
306 assertThat(newMap.entrySet(), hasSize(1));
307
308 assertThat(newMap.replace(KEY1, VALUE4).value(), is(VALUE1));
309 assertThat(newMap.get(KEY1).value(), is(VALUE4));
310
311 assertThat(newMap.replace(KEY1, VALUE4, VALUE2), is(true));
312 assertThat(newMap.get(KEY1).value(), is(VALUE2));
313
314 assertThat(newMap.replace(KEY1, DEFAULT_VERSION, VALUE1), is(true));
315 assertThat(newMap.get(KEY1).value(), is(VALUE1));
316
317 newMap.clear();
318 assertThat(newMap.size(), is(0));
319
320 newMap.compute(KEY1, (a, b) -> VALUE1);
321 assertThat(newMap.get(KEY1).value(), is(VALUE1));
322 newMap.computeIfPresent(KEY1, (a, b) -> VALUE2);
323 assertThat(newMap.get(KEY1).value(), is(VALUE2));
324
325 Listener listener1 = new Listener(1);
326 newMap.addListener(listener1, null);
327 assertThat(asyncMap.listeners, hasSize(1));
328 assertThat(asyncMap.listeners, hasItem(listener1));
329
330 newMap.removeListener(listener1);
331 assertThat(asyncMap.listeners, hasSize(0));
332
333 Consumer<DistributedPrimitive.Status> consumer = status -> { };
334
335 newMap.addStatusChangeListener(consumer);
336 assertThat(newMap.statusChangeListeners(), hasSize(1));
337 assertThat(newMap.statusChangeListeners(), hasItem(consumer));
338
339 newMap.removeStatusChangeListener(consumer);
340 assertThat(newMap.statusChangeListeners(), hasSize(0));
341 assertThat(newMap.statusChangeListeners(), not(hasItem(consumer)));
342 }
343
344}