blob: 1548fa5afb8de422e34bae0d97cc334b3e25bf91 [file] [log] [blame]
Aaron Kruglikov3e29f662016-07-13 10:18:10 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Aaron Kruglikov3e29f662016-07-13 10:18:10 -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.resources.impl;
17
Jordan Halterman2bf177c2017-06-29 01:49:08 -070018import com.google.common.collect.Lists;
19import io.atomix.protocols.raft.proxy.RaftProxy;
20import io.atomix.protocols.raft.service.RaftService;
21import org.junit.Test;
22import org.onlab.util.Tools;
Jordan Haltermandae11602018-07-03 00:00:47 -070023import org.onosproject.store.service.AsyncIterator;
Jordan Halterman2bf177c2017-06-29 01:49:08 -070024import org.onosproject.store.service.MapEvent;
25import org.onosproject.store.service.MapEventListener;
Jordan Haltermandae11602018-07-03 00:00:47 -070026import org.onosproject.store.service.Versioned;
Jordan Halterman2bf177c2017-06-29 01:49:08 -070027
Jordan Haltermandae11602018-07-03 00:00:47 -070028import java.util.ArrayList;
Ray Milkey6a51cb92018-03-06 09:03:03 -080029import java.util.Arrays;
30import java.util.Collection;
31import java.util.List;
Jordan Haltermandae11602018-07-03 00:00:47 -070032import java.util.Map;
33import java.util.UUID;
Ray Milkey6a51cb92018-03-06 09:03:03 -080034import java.util.concurrent.ArrayBlockingQueue;
35import java.util.concurrent.BlockingQueue;
Jordan Haltermandae11602018-07-03 00:00:47 -070036import java.util.concurrent.TimeUnit;
Ray Milkey6a51cb92018-03-06 09:03:03 -080037import java.util.stream.Collectors;
38
Aaron Kruglikov3e29f662016-07-13 10:18:10 -070039import static org.junit.Assert.assertArrayEquals;
40import static org.junit.Assert.assertEquals;
41import static org.junit.Assert.assertFalse;
Jordan Haltermanf6272442017-04-20 02:18:08 -070042import static org.junit.Assert.assertNotEquals;
Aaron Kruglikov3e29f662016-07-13 10:18:10 -070043import static org.junit.Assert.assertNotNull;
44import static org.junit.Assert.assertNull;
45import static org.junit.Assert.assertTrue;
46
47/**
48 * Unit tests for {@link AtomixConsistentTreeMap}.
49 */
Jordan Halterman2bf177c2017-06-29 01:49:08 -070050public class AtomixConsistentTreeMapTest extends AtomixTestBase<AtomixConsistentTreeMap> {
Aaron Kruglikov3e29f662016-07-13 10:18:10 -070051 private final String keyFour = "hello";
52 private final String keyThree = "goodbye";
53 private final String keyTwo = "foo";
54 private final String keyOne = "bar";
55 private final byte[] valueOne = Tools.getBytesUtf8(keyOne);
56 private final byte[] valueTwo = Tools.getBytesUtf8(keyTwo);
57 private final byte[] valueThree = Tools.getBytesUtf8(keyThree);
58 private final byte[] valueFour = Tools.getBytesUtf8(keyFour);
59 private final byte[] spareValue = Tools.getBytesUtf8("spareValue");
60 private final List<String> allKeys = Lists.newArrayList(keyOne, keyTwo,
61 keyThree, keyFour);
62 private final List<byte[]> allValues = Lists.newArrayList(valueOne,
63 valueTwo,
64 valueThree,
65 valueFour);
Aaron Kruglikov3e29f662016-07-13 10:18:10 -070066
Jordan Halterman2bf177c2017-06-29 01:49:08 -070067 @Override
68 protected RaftService createService() {
69 return new AtomixConsistentTreeMapService();
Aaron Kruglikov3e29f662016-07-13 10:18:10 -070070 }
71
72 @Override
Jordan Halterman2bf177c2017-06-29 01:49:08 -070073 protected AtomixConsistentTreeMap createPrimitive(RaftProxy proxy) {
74 return new AtomixConsistentTreeMap(proxy);
Aaron Kruglikov3e29f662016-07-13 10:18:10 -070075 }
76
77 /**
78 * Tests of the functionality associated with the
79 * {@link org.onosproject.store.service.AsyncConsistentMap} interface
80 * except transactions and listeners.
81 */
82 @Test
83 public void testBasicMapOperations() throws Throwable {
84 //Throughout the test there are isEmpty queries, these are intended to
85 //make sure that the previous section has been cleaned up, they serve
86 //the secondary purpose of testing isEmpty but that is not their
87 //primary purpose.
88 AtomixConsistentTreeMap map = createResource("basicTestMap");
89 //test size
90 map.size().thenAccept(result -> assertEquals(0, (int) result)).join();
91 map.isEmpty().thenAccept(result -> assertTrue(result)).join();
Jordan Haltermanf6272442017-04-20 02:18:08 -070092
Aaron Kruglikov3e29f662016-07-13 10:18:10 -070093 //test contains key
94 allKeys.forEach(key -> map.containsKey(key).
95 thenAccept(result -> assertFalse(result)).join());
Jordan Haltermanf6272442017-04-20 02:18:08 -070096
Aaron Kruglikov3e29f662016-07-13 10:18:10 -070097 //test contains value
98 allValues.forEach(value -> map.containsValue(value)
99 .thenAccept(result -> assertFalse(result)).join());
Jordan Haltermanf6272442017-04-20 02:18:08 -0700100
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700101 //test get
102 allKeys.forEach(key -> map.get(key).
103 thenAccept(result -> assertNull(result)).join());
104
Jordan Haltermanf6272442017-04-20 02:18:08 -0700105 //test getOrDefault
106 allKeys.forEach(key -> map.getOrDefault(key, null).thenAccept(result -> {
107 assertEquals(0, result.version());
108 assertNull(result.value());
109 }).join());
110
111 allKeys.forEach(key -> map.getOrDefault(key, "bar".getBytes()).thenAccept(result -> {
112 assertEquals(0, result.version());
113 assertArrayEquals("bar".getBytes(), result.value());
114 }).join());
115
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700116 //populate and redo prior three tests
Jordan Haltermanf6272442017-04-20 02:18:08 -0700117 allKeys.forEach(key -> map.put(key, allValues.get(allKeys.indexOf(key)))
118 .thenAccept(result -> assertNull(result)).join());
119
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700120 //test contains key
Jordan Haltermanf6272442017-04-20 02:18:08 -0700121 allKeys.forEach(key -> map.containsKey(key)
122 .thenAccept(result -> assertTrue(result)).join());
123
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700124 //test contains value
125 allValues.forEach(value -> map.containsValue(value)
126 .thenAccept(result -> assertTrue(result)).join());
Jordan Haltermanf6272442017-04-20 02:18:08 -0700127
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700128 //test get
Jordan Haltermanf6272442017-04-20 02:18:08 -0700129 allKeys.forEach(key -> map.get(key).thenAccept(result -> {
130 assertArrayEquals(allValues.get(allKeys.indexOf(key)), result.value());
131 }).join());
132
133 allKeys.forEach(key -> map.getOrDefault(key, null).thenAccept(result -> {
134 assertNotEquals(0, result.version());
135 assertArrayEquals(allValues.get(allKeys.indexOf(key)), result.value());
136 }).join());
137
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700138 //test all compute methods in this section
Jordan Haltermanf6272442017-04-20 02:18:08 -0700139 allKeys.forEach(key -> map.computeIfAbsent(key, v -> allValues.get(allKeys.indexOf(key)))
140 .thenAccept(result -> {
141 assertArrayEquals(allValues.get(allKeys.indexOf(key)), result.value());
142 }).join());
143
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700144 map.size().thenAccept(result -> assertEquals(4, (int) result)).join();
145 map.isEmpty().thenAccept(result -> assertFalse(result)).join();
Jordan Haltermanf6272442017-04-20 02:18:08 -0700146
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700147 allKeys.forEach(key -> map.computeIfPresent(key, (k, v) -> null).
148 thenAccept(result -> assertNull(result)).join());
Jordan Haltermanf6272442017-04-20 02:18:08 -0700149
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700150 map.isEmpty().thenAccept(result -> assertTrue(result)).join();
Jordan Haltermanf6272442017-04-20 02:18:08 -0700151
152 allKeys.forEach(key -> map.compute(key, (k, v) -> allValues.get(allKeys.indexOf(key)))
153 .thenAccept(result -> assertArrayEquals(allValues.get(allKeys.indexOf(key)), result.value())).join());
154
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700155 map.size().thenAccept(result -> assertEquals(4, (int) result)).join();
156 map.isEmpty().thenAccept(result -> assertFalse(result)).join();
Jordan Haltermanf6272442017-04-20 02:18:08 -0700157
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700158 allKeys.forEach(key -> map.computeIf(key,
Jordan Haltermanf6272442017-04-20 02:18:08 -0700159 (k) -> allKeys.indexOf(key) < 2, (k, v) -> null).thenAccept(result -> {
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700160 if (allKeys.indexOf(key) < 2) {
161 assertNull(result);
162 } else {
Jordan Haltermanf6272442017-04-20 02:18:08 -0700163 assertArrayEquals(allValues.get(allKeys.indexOf(key)), result.value());
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700164 }
Jordan Haltermanf6272442017-04-20 02:18:08 -0700165 }).join());
166
167 map.size().thenAccept(result -> assertEquals(2, (int) result)).join();
168 map.isEmpty().thenAccept(result -> assertFalse(result)).join();
169
170 //test simple put
171 allKeys.forEach(key -> map.put(key, allValues.get(allKeys.indexOf(key))).thenAccept(result -> {
172 if (allKeys.indexOf(key) < 2) {
173 assertNull(result);
174 } else {
175 assertArrayEquals(allValues.get(allKeys.indexOf(key)), result.value());
176 }
177 }).join());
178
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700179 map.size().thenAccept(result -> assertEquals(4, (int) result)).join();
180 map.isEmpty().thenAccept(result -> assertFalse(result)).join();
Jordan Haltermanf6272442017-04-20 02:18:08 -0700181
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700182 //test put and get for version retrieval
Jordan Haltermanf6272442017-04-20 02:18:08 -0700183 allKeys.forEach(key -> map.putAndGet(key, allValues.get(allKeys.indexOf(key))).thenAccept(firstResult -> {
184 map.putAndGet(key, allValues.get(allKeys.indexOf(key))).thenAccept(secondResult -> {
185 assertArrayEquals(allValues.get(allKeys.indexOf(key)), firstResult.value());
186 assertArrayEquals(allValues.get(allKeys.indexOf(key)), secondResult.value());
Jordan Haltermanf6272442017-04-20 02:18:08 -0700187 });
188 }).join());
189
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700190 //test removal
191 allKeys.forEach(key -> map.remove(key).thenAccept(
192 result -> assertArrayEquals(
193 allValues.get(allKeys.indexOf(key)), result.value()))
194 .join());
195 map.isEmpty().thenAccept(result -> assertTrue(result));
Jordan Haltermanf6272442017-04-20 02:18:08 -0700196
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700197 //repopulating, this is not mainly for testing
Jordan Haltermanf6272442017-04-20 02:18:08 -0700198 allKeys.forEach(key -> map.put(key, allValues.get(allKeys.indexOf(key))).thenAccept(result -> {
199 assertNull(result);
200 }).join());
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700201
202 //Test various collections of keys, values and entries
Jordan Haltermanf6272442017-04-20 02:18:08 -0700203 map.keySet().thenAccept(keys -> assertTrue(stringArrayCollectionIsEqual(keys, allKeys))).join();
204 map.values().thenAccept(values -> assertTrue(
205 byteArrayCollectionIsEqual(values.stream().map(v -> v.value())
206 .collect(Collectors.toSet()), allValues))).join();
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700207 map.entrySet().thenAccept(entrySet -> {
208 entrySet.forEach(entry -> {
209 assertTrue(allKeys.contains(entry.getKey()));
210 assertTrue(Arrays.equals(entry.getValue().value(),
211 allValues.get(allKeys.indexOf(entry.getKey()))));
212 });
213 }).join();
214 map.clear().join();
215 map.isEmpty().thenAccept(result -> assertTrue(result)).join();
216
217 //test conditional put
Jordan Haltermanf6272442017-04-20 02:18:08 -0700218 allKeys.forEach(key -> map.putIfAbsent(key, allValues.get(allKeys.indexOf(key)))
219 .thenAccept(result -> assertNull(result)).join());
220 allKeys.forEach(key -> map.putIfAbsent(key, null).thenAccept(result ->
221 assertArrayEquals(result.value(), allValues.get(allKeys.indexOf(key)))
222 ).join());
223
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700224 // test alternate removes that specify value or version
Jordan Haltermanf6272442017-04-20 02:18:08 -0700225 allKeys.forEach(key -> map.remove(key, spareValue).thenAccept(result -> assertFalse(result)).join());
226 allKeys.forEach(key -> map.remove(key, allValues.get(allKeys.indexOf(key)))
227 .thenAccept(result -> assertTrue(result)).join());
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700228 map.isEmpty().thenAccept(result -> assertTrue(result)).join();
229 List<Long> versions = Lists.newArrayList();
230
231 //repopulating set for version based removal
Jordan Haltermanf6272442017-04-20 02:18:08 -0700232 allKeys.forEach(key -> map.putAndGet(key, allValues.get(allKeys.indexOf(key)))
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700233 .thenAccept(result -> versions.add(result.version())).join());
Jordan Haltermanf6272442017-04-20 02:18:08 -0700234 allKeys.forEach(key -> map.remove(key, versions.get(0)).thenAccept(result -> {
235 assertTrue(result);
236 versions.remove(0);
237 }).join());
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700238 map.isEmpty().thenAccept(result -> assertTrue(result)).join();
Jordan Haltermanf6272442017-04-20 02:18:08 -0700239
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700240 //Testing all replace both simple (k, v), and complex that consider
241 // previous mapping or version.
Jordan Haltermanf6272442017-04-20 02:18:08 -0700242 allKeys.forEach(key -> map.put(key, allValues.get(allKeys.indexOf(key)))
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700243 .thenAccept(result -> assertNull(result)).join());
Jordan Haltermanf6272442017-04-20 02:18:08 -0700244 allKeys.forEach(key -> map.replace(key, allValues.get(3 - allKeys.indexOf(key)))
245 .thenAccept(result -> assertArrayEquals(allValues.get(allKeys.indexOf(key)), result.value()))
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700246 .join());
Jordan Haltermanf6272442017-04-20 02:18:08 -0700247 allKeys.forEach(key -> map.replace(key, spareValue, allValues.get(allKeys.indexOf(key)))
248 .thenAccept(result -> assertFalse(result)).join());
249 allKeys.forEach(key -> map.replace(key, allValues.get(3 - allKeys.indexOf(key)),
250 allValues.get(allKeys.indexOf(key))).thenAccept(result -> assertTrue(result)).join());
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700251 map.clear().join();
252 map.isEmpty().thenAccept(result -> assertTrue(result)).join();
253 versions.clear();
Jordan Haltermanf6272442017-04-20 02:18:08 -0700254
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700255 //populate for version based replacement
Jordan Haltermanf6272442017-04-20 02:18:08 -0700256 allKeys.forEach(key -> map.putAndGet(key, allValues.get(3 - allKeys.indexOf(key)))
257 .thenAccept(result -> versions.add(result.version())).join());
258 allKeys.forEach(key -> map.replace(key, 0, allValues.get(allKeys.indexOf(key)))
259 .thenAccept(result -> assertFalse(result)).join());
260 allKeys.forEach(key -> map.replace(key, versions.get(0), allValues.get(allKeys.indexOf(key)))
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700261 .thenAccept(result -> {
262 assertTrue(result);
263 versions.remove(0);
264 }).join());
265 }
266
267 @Test
268 public void mapListenerTests() throws Throwable {
269 final byte[] value1 = Tools.getBytesUtf8("value1");
270 final byte[] value2 = Tools.getBytesUtf8("value2");
271 final byte[] value3 = Tools.getBytesUtf8("value3");
272
273 AtomixConsistentTreeMap map = createResource("treeMapListenerTestMap");
274 TestMapEventListener listener = new TestMapEventListener();
275
276 // add listener; insert new value into map and verify an INSERT event
277 // is received.
278 map.addListener(listener).thenCompose(v -> map.put("foo", value1))
279 .join();
280 MapEvent<String, byte[]> event = listener.event();
281 assertNotNull(event);
282 assertEquals(MapEvent.Type.INSERT, event.type());
283 assertTrue(Arrays.equals(value1, event.newValue().value()));
284
285 // remove listener and verify listener is not notified.
286 map.removeListener(listener).thenCompose(v -> map.put("foo", value2))
287 .join();
288 assertFalse(listener.eventReceived());
289
290 // add the listener back and verify UPDATE events are received
291 // correctly
292 map.addListener(listener).thenCompose(v -> map.put("foo", value3))
293 .join();
294 event = listener.event();
295 assertNotNull(event);
296 assertEquals(MapEvent.Type.UPDATE, event.type());
297 assertTrue(Arrays.equals(value3, event.newValue().value()));
298
299 // perform a non-state changing operation and verify no events are
300 // received.
301 map.putIfAbsent("foo", value1).join();
302 assertFalse(listener.eventReceived());
303
304 // verify REMOVE events are received correctly.
305 map.remove("foo").join();
306 event = listener.event();
307 assertNotNull(event);
308 assertEquals(MapEvent.Type.REMOVE, event.type());
309 assertTrue(Arrays.equals(value3, event.oldValue().value()));
310
311 // verify compute methods also generate events.
312 map.computeIf("foo", v -> v == null, (k, v) -> value1).join();
313 event = listener.event();
314 assertNotNull(event);
315 assertEquals(MapEvent.Type.INSERT, event.type());
316 assertTrue(Arrays.equals(value1, event.newValue().value()));
317
318 map.compute("foo", (k, v) -> value2).join();
319 event = listener.event();
320 assertNotNull(event);
321 assertEquals(MapEvent.Type.UPDATE, event.type());
322 assertTrue(Arrays.equals(value2, event.newValue().value()));
323
324 map.computeIf(
325 "foo", v -> Arrays.equals(v, value2), (k, v) -> null).join();
326 event = listener.event();
327 assertNotNull(event);
328 assertEquals(MapEvent.Type.REMOVE, event.type());
329 assertTrue(Arrays.equals(value2, event.oldValue().value()));
330
331 map.removeListener(listener).join();
332 }
333
334 /**
335 * Tests functionality specified in the {@link AtomixConsistentTreeMap}
336 * interface, beyond the functionality provided in
337 * {@link org.onosproject.store.service.AsyncConsistentMap}.
338 */
339 @Test
340 public void treeMapFunctionsTest() {
341 AtomixConsistentTreeMap map = createResource("treeMapFunctionTestMap");
342 //Tests on empty map
343 map.firstKey().thenAccept(result -> assertNull(result)).join();
344 map.lastKey().thenAccept(result -> assertNull(result)).join();
345 map.ceilingEntry(keyOne).thenAccept(result -> assertNull(result))
346 .join();
347 map.floorEntry(keyOne).thenAccept(result -> assertNull(result)).join();
348 map.higherEntry(keyOne).thenAccept(result -> assertNull(result))
349 .join();
350 map.lowerEntry(keyOne).thenAccept(result -> assertNull(result)).join();
351 map.firstEntry().thenAccept(result -> assertNull(result)).join();
352 map.lastEntry().thenAccept(result -> assertNull(result)).join();
353 map.pollFirstEntry().thenAccept(result -> assertNull(result)).join();
354 map.pollLastEntry().thenAccept(result -> assertNull(result)).join();
355 map.lowerKey(keyOne).thenAccept(result -> assertNull(result)).join();
356 map.floorKey(keyOne).thenAccept(result -> assertNull(result)).join();
357 map.ceilingKey(keyOne).thenAccept(result -> assertNull(result))
358 .join();
359 map.higherKey(keyOne).thenAccept(result -> assertNull(result)).join();
Jordan Halterman2bf177c2017-06-29 01:49:08 -0700360
361 // TODO: delete() is not supported
362 //map.delete().join();
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700363
364 allKeys.forEach(key -> map.put(
365 key, allValues.get(allKeys.indexOf(key)))
366 .thenAccept(result -> assertNull(result)).join());
367 //Note ordering keys are in their proper ordering in ascending order
368 //both in naming and in the allKeys list.
369
370 map.firstKey().thenAccept(result -> assertEquals(keyOne, result))
371 .join();
372 map.lastKey().thenAccept(result -> assertEquals(keyFour, result))
373 .join();
374 map.ceilingEntry(keyOne)
375 .thenAccept(result -> {
376 assertEquals(keyOne, result.getKey());
377 assertArrayEquals(valueOne, result.getValue().value());
378 })
379 .join();
380 //adding an additional letter to make keyOne an unacceptable response
381 map.ceilingEntry(keyOne + "a")
382 .thenAccept(result -> {
383 assertEquals(keyTwo, result.getKey());
384 assertArrayEquals(valueTwo, result.getValue().value());
385 })
386 .join();
387 map.ceilingEntry(keyFour + "a")
388 .thenAccept(result -> {
389 assertNull(result);
390 })
391 .join();
392 map.floorEntry(keyTwo).thenAccept(result -> {
393 assertEquals(keyTwo, result.getKey());
394 assertArrayEquals(valueTwo, result.getValue().value());
395 })
396 .join();
397 //shorten the key so it itself is not an acceptable reply
398 map.floorEntry(keyTwo.substring(0, 2)).thenAccept(result -> {
399 assertEquals(keyOne, result.getKey());
400 assertArrayEquals(valueOne, result.getValue().value());
401 })
402 .join();
403 // shorten least key so no acceptable response exists
404 map.floorEntry(keyOne.substring(0, 1)).thenAccept(
405 result -> assertNull(result))
406 .join();
407
408 map.higherEntry(keyTwo).thenAccept(result -> {
409 assertEquals(keyThree, result.getKey());
410 assertArrayEquals(valueThree, result.getValue().value());
411 })
412 .join();
413 map.higherEntry(keyFour).thenAccept(result -> assertNull(result))
414 .join();
415
416 map.lowerEntry(keyFour).thenAccept(result -> {
417 assertEquals(keyThree, result.getKey());
418 assertArrayEquals(valueThree, result.getValue().value());
419 })
420 .join();
421 map.lowerEntry(keyOne).thenAccept(result -> assertNull(result))
422 .join();
423 map.firstEntry().thenAccept(result -> {
424 assertEquals(keyOne, result.getKey());
425 assertArrayEquals(valueOne, result.getValue().value());
426 })
427 .join();
428 map.lastEntry().thenAccept(result -> {
429 assertEquals(keyFour, result.getKey());
430 assertArrayEquals(valueFour, result.getValue().value());
431 })
432 .join();
433 map.pollFirstEntry().thenAccept(result -> {
434 assertEquals(keyOne, result.getKey());
435 assertArrayEquals(valueOne, result.getValue().value());
436 });
437 map.containsKey(keyOne).thenAccept(result -> assertFalse(result))
438 .join();
439 map.size().thenAccept(result -> assertEquals(3, (int) result)).join();
440 map.pollLastEntry().thenAccept(result -> {
441 assertEquals(keyFour, result.getKey());
442 assertArrayEquals(valueFour, result.getValue().value());
443 });
444 map.containsKey(keyFour).thenAccept(result -> assertFalse(result))
445 .join();
446 map.size().thenAccept(result -> assertEquals(2, (int) result)).join();
447
448 //repopulate the missing entries
449 allKeys.forEach(key -> map.put(
450 key, allValues.get(allKeys.indexOf(key)))
451 .thenAccept(result -> {
452 if (key.equals(keyOne) || key.equals(keyFour)) {
453 assertNull(result);
454 } else {
455 assertArrayEquals(allValues.get(allKeys.indexOf(key)),
456 result.value());
457 }
458 })
459 .join());
460 map.lowerKey(keyOne).thenAccept(result -> assertNull(result)).join();
461 map.lowerKey(keyThree).thenAccept(
462 result -> assertEquals(keyTwo, result))
463 .join();
464 map.floorKey(keyThree).thenAccept(
465 result -> assertEquals(keyThree, result))
466 .join();
467 //shortening the key so there is no acceptable response
468 map.floorKey(keyOne.substring(0, 1)).thenAccept(
469 result -> assertNull(result))
470 .join();
471 map.ceilingKey(keyTwo).thenAccept(
472 result -> assertEquals(keyTwo, result))
473 .join();
474 //adding to highest key so there is no acceptable response
475 map.ceilingKey(keyFour + "a")
Jon Hall7d77fe12018-04-24 18:03:10 -0700476 .thenAccept(result -> assertNull(result))
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700477 .join();
478 map.higherKey(keyThree).thenAccept(
479 result -> assertEquals(keyFour, result))
480 .join();
481 map.higherKey(keyFour).thenAccept(
482 result -> assertNull(result))
483 .join();
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700484
Jordan Halterman2bf177c2017-06-29 01:49:08 -0700485 // TODO: delete() is not supported
486 //map.delete().join();
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700487 }
488
Jordan Haltermandae11602018-07-03 00:00:47 -0700489 @Test
490 public void testIterator() throws Exception {
491 AtomixConsistentTreeMap map = newPrimitive("testIterator");
492 for (int i = 0; i < 100; i++) {
493 for (int j = 0; j < 100; j++) {
494 map.put(String.valueOf(i), String.valueOf(j).getBytes()).join();
495 }
496 }
497
498 List<Map.Entry<String, Versioned<byte[]>>> entries = new ArrayList<>();
499 AsyncIterator<Map.Entry<String, Versioned<byte[]>>> iterator = map.iterator().get(5, TimeUnit.SECONDS);
500 while (iterator.hasNext().get(5, TimeUnit.SECONDS)) {
501 map.put("foo", UUID.randomUUID().toString().getBytes()).join();
502 entries.add(iterator.next().get(5, TimeUnit.SECONDS));
503 }
504 assertEquals(100, entries.size());
505 assertEquals(101, map.asConsistentMap().stream().count());
506 }
507
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700508 private AtomixConsistentTreeMap createResource(String mapName) {
509 try {
Jordan Halterman2bf177c2017-06-29 01:49:08 -0700510 AtomixConsistentTreeMap map = newPrimitive(mapName);
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700511 return map;
512 } catch (Throwable e) {
Ray Milkeydbd38212018-07-02 09:18:09 -0700513 throw new IllegalStateException(e.toString());
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700514 }
515 }
516 private static class TestMapEventListener
517 implements MapEventListener<String, byte[]> {
518
519 private final BlockingQueue<MapEvent<String, byte[]>> queue =
520 new ArrayBlockingQueue<>(1);
521
522 @Override
523 public void event(MapEvent<String, byte[]> event) {
524 try {
525 queue.put(event);
526 } catch (InterruptedException e) {
Ray Milkey6a51cb92018-03-06 09:03:03 -0800527 Thread.interrupted();
528 throw new IllegalStateException(e);
Aaron Kruglikov3e29f662016-07-13 10:18:10 -0700529 }
530 }
531
532 public boolean eventReceived() {
533 return !queue.isEmpty();
534 }
535
536 public MapEvent<String, byte[]> event() throws InterruptedException {
537 return queue.take();
538 }
539 }
540
541 /**
542 * Returns two arrays contain the same set of elements,
543 * regardless of order.
544 * @param o1 first collection
545 * @param o2 second collection
546 * @return true if they contain the same elements
547 */
548 private boolean byteArrayCollectionIsEqual(
549 Collection<? extends byte[]> o1, Collection<? extends byte[]> o2) {
550 if (o1 == null || o2 == null || o1.size() != o2.size()) {
551 return false;
552 }
553 for (byte[] array1 : o1) {
554 boolean matched = false;
555 for (byte[] array2 : o2) {
556 if (Arrays.equals(array1, array2)) {
557 matched = true;
558 break;
559 }
560 }
561 if (!matched) {
562 return false;
563 }
564 }
565 return true;
566 }
567
568 /**
569 * Compares two collections of strings returns true if they contain the
570 * same strings, false otherwise.
571 * @param s1 string collection one
572 * @param s2 string collection two
573 * @return true if the two sets contain the same strings
574 */
575 private boolean stringArrayCollectionIsEqual(
576 Collection<? extends String> s1, Collection<? extends String> s2) {
577 if (s1 == null || s2 == null || s1.size() != s2.size()) {
578 return false;
579 }
580 for (String string1 : s1) {
581 boolean matched = false;
582 for (String string2 : s2) {
583 if (string1.equals(string2)) {
584 matched = true;
585 break;
586 }
587 }
588 if (!matched) {
589 return false;
590 }
591 }
592 return true;
593 }
Ray Milkey88cc3432017-03-30 17:19:08 -0700594}