blob: e7f932b89e97e082b966a669d70d36d1f0e260f4 [file] [log] [blame]
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -07001/*
2 * Copyright 2016 Open Networking Laboratory
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.resources.impl;
18
19import com.google.common.collect.Lists;
20import com.google.common.collect.Multiset;
21import com.google.common.collect.TreeMultiset;
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -070022import io.atomix.resource.ResourceType;
23import org.apache.commons.collections.keyvalue.DefaultMapEntry;
Aaron Kruglikovb5a41e52016-06-23 15:37:41 -070024import org.junit.AfterClass;
25import org.junit.BeforeClass;
Aaron Kruglikov43d843d2016-07-01 06:39:38 -070026import org.junit.Ignore;
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -070027import org.junit.Test;
28import org.onlab.util.Tools;
29
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -070030import java.util.Arrays;
31import java.util.Collection;
32import java.util.Comparator;
33import java.util.List;
34import java.util.Map;
35
36import static org.junit.Assert.assertEquals;
37import static org.junit.Assert.assertFalse;
38import static org.junit.Assert.assertTrue;
39
40/**
41 * Tests the {@link AsyncConsistentSetMultimap}.
42 */
Aaron Kruglikov43d843d2016-07-01 06:39:38 -070043@Ignore
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -070044public class AsyncConsistentSetMultimapTest extends AtomixTestBase {
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -070045 private final String keyOne = "hello";
46 private final String keyTwo = "goodbye";
47 private final String keyThree = "foo";
48 private final String keyFour = "bar";
49 private final byte[] valueOne = Tools.getBytesUtf8(keyOne);
50 private final byte[] valueTwo = Tools.getBytesUtf8(keyTwo);
51 private final byte[] valueThree = Tools.getBytesUtf8(keyThree);
52 private final byte[] valueFour = Tools.getBytesUtf8(keyFour);
53 private final List<String> allKeys = Lists.newArrayList(keyOne, keyTwo,
54 keyThree, keyFour);
55 private final List<byte[]> allValues = Lists.newArrayList(valueOne,
56 valueTwo,
57 valueThree,
58 valueFour);
Aaron Kruglikovb5a41e52016-06-23 15:37:41 -070059
60 @BeforeClass
61 public static void preTestSetup() throws Throwable {
62 createCopycatServers(3);
63 }
64
65 @AfterClass
66 public static void postTestCleanup() throws Exception {
67 clearTests();
68 }
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -070069
70 @Override
71 protected ResourceType resourceType() {
72 return new ResourceType(AsyncConsistentSetMultimap.class);
73 }
74
75 /**
76 * Test that size behaves correctly (This includes testing of the empty
77 * check).
78 */
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -070079 @Test
80 public void testSize() throws Throwable {
Aaron Kruglikovb5a41e52016-06-23 15:37:41 -070081 AsyncConsistentSetMultimap map = createResource("testOneMap");
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -070082 //Simplest operation case
83 map.isEmpty().thenAccept(result -> assertTrue(result));
84 map.put(keyOne, valueOne).
85 thenAccept(result -> assertTrue(result)).join();
86 map.isEmpty().thenAccept(result -> assertFalse(result));
87 map.size().thenAccept(result -> assertEquals(1, (int) result))
88 .join();
89 //Make sure sizing is dependent on values not keys
90 map.put(keyOne, valueTwo).
91 thenAccept(result -> assertTrue(result)).join();
92 map.size().thenAccept(result -> assertEquals(2, (int) result))
93 .join();
94 //Ensure that double adding has no effect
95 map.put(keyOne, valueOne).
96 thenAccept(result -> assertFalse(result)).join();
97 map.size().thenAccept(result -> assertEquals(2, (int) result))
98 .join();
99 //Check handling for multiple keys
100 map.put(keyTwo, valueOne)
101 .thenAccept(result -> assertTrue(result)).join();
102 map.put(keyTwo, valueTwo)
103 .thenAccept(result -> assertTrue(result)).join();
104 map.size().thenAccept(result -> assertEquals(4, (int) result))
105 .join();
106 //Check size with removal
107 map.remove(keyOne, valueOne).
108 thenAccept(result -> assertTrue(result)).join();
109 map.size().thenAccept(result -> assertEquals(3, (int) result))
110 .join();
111 //Check behavior under remove of non-existant key
112 map.remove(keyOne, valueOne).
113 thenAccept(result -> assertFalse(result)).join();
114 map.size().thenAccept(result -> assertEquals(3, (int) result))
115 .join();
116 //Check clearing the entirety of the map
117 map.clear().join();
118 map.size().thenAccept(result -> assertEquals(0, (int) result))
119 .join();
120 map.isEmpty().thenAccept(result -> assertTrue(result));
121
122 map.destroy().join();
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -0700123 }
124
125 /**
126 * Contains tests for value, key and entry.
127 */
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -0700128 @Test
129 public void containsTest() throws Throwable {
Aaron Kruglikovb5a41e52016-06-23 15:37:41 -0700130 AsyncConsistentSetMultimap map = createResource("testTwoMap");
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -0700131
132 //Populate the maps
133 allKeys.forEach(key -> {
134 map.putAll(key, allValues)
135 .thenAccept(result -> assertTrue(result)).join();
136 });
137 map.size().thenAccept(result -> assertEquals(16, (int) result)).join();
138
139 //Test key contains positive results
140 allKeys.forEach(key -> {
141 map.containsKey(key)
142 .thenAccept(result -> assertTrue(result)).join();
143 });
144
145 //Test value contains positive results
146 allValues.forEach(value -> {
147 map.containsValue(value)
148 .thenAccept(result -> assertTrue(result)).join();
149 });
150
151 //Test contains entry for all possible entries
152 allKeys.forEach(key -> {
153 allValues.forEach(value -> {
154 map.containsEntry(key, value)
155 .thenAccept(result -> assertTrue(result)).join();
156 });
157 });
158
159 //Test behavior after removals
160 allValues.forEach(value -> {
161 final String[] removedKey = new String[1];
162 allKeys.forEach(key -> {
163 map.remove(key, value)
164 .thenAccept(result -> assertTrue(result)).join();
165 map.containsEntry(key, value)
166 .thenAccept(result -> assertFalse(result)).join();
167 removedKey[0] = key;
168 });
169 //Check that contains key works properly for removed keys
170 map.containsKey(removedKey[0])
171 .thenAccept(result -> assertFalse(result));
172 });
173
174 //Check that contains value works correctly for removed values
175 allValues.forEach(value -> {
176 map.containsValue(value)
177 .thenAccept(result -> assertFalse(result)).join();
178 });
179
180 map.destroy().join();
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -0700181 }
182
183 /**
184 * Contains tests for put, putAll, remove, removeAll and replace.
185 * @throws Exception
186 */
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -0700187 @Test
188 public void addAndRemoveTest() throws Exception {
Aaron Kruglikovb5a41e52016-06-23 15:37:41 -0700189 AsyncConsistentSetMultimap map = createResource("testThreeMap");
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -0700190
191 //Test single put
192 allKeys.forEach(key -> {
193 //Value should actually be added here
194 allValues.forEach(value -> {
195 map.put(key, value)
196 .thenAccept(result -> assertTrue(result)).join();
197 //Duplicate values should be ignored here
198 map.put(key, value)
199 .thenAccept(result -> assertFalse(result)).join();
200 });
201 });
202
203 //Test single remove
204 allKeys.forEach(key -> {
205 //Value should actually be added here
206 allValues.forEach(value -> {
207 map.remove(key, value)
208 .thenAccept(result -> assertTrue(result)).join();
209 //Duplicate values should be ignored here
210 map.remove(key, value)
211 .thenAccept(result -> assertFalse(result)).join();
212 });
213 });
214
215 map.isEmpty().thenAccept(result -> assertTrue(result)).join();
216
217 //Test multi put
218 allKeys.forEach(key -> {
219 map.putAll(key, Lists.newArrayList(allValues.subList(0, 2)))
220 .thenAccept(result -> assertTrue(result)).join();
221 map.putAll(key, Lists.newArrayList(allValues.subList(0, 2)))
222 .thenAccept(result -> assertFalse(result)).join();
223 map.putAll(key, Lists.newArrayList(allValues.subList(2, 4)))
224 .thenAccept(result -> assertTrue(result)).join();
225 map.putAll(key, Lists.newArrayList(allValues.subList(2, 4)))
226 .thenAccept(result -> assertFalse(result)).join();
227
228 });
229
230 //Test multi remove
231 allKeys.forEach(key -> {
232 //Split the lists to test how multiRemove can work piecewise
233 map.removeAll(key, Lists.newArrayList(allValues.subList(0, 2)))
234 .thenAccept(result -> assertTrue(result)).join();
235 map.removeAll(key, Lists.newArrayList(allValues.subList(0, 2)))
236 .thenAccept(result -> assertFalse(result)).join();
237 map.removeAll(key, Lists.newArrayList(allValues.subList(2, 4)))
238 .thenAccept(result -> assertTrue(result)).join();
239 map.removeAll(key, Lists.newArrayList(allValues.subList(2, 4)))
240 .thenAccept(result -> assertFalse(result)).join();
241 });
242
243 map.isEmpty().thenAccept(result -> assertTrue(result)).join();
244
245 //Repopulate for next test
246 allKeys.forEach(key -> {
247 map.putAll(key, allValues)
248 .thenAccept(result -> assertTrue(result)).join();
249 });
250
251 map.size().thenAccept(result -> assertEquals(16, (int) result)).join();
252
253 //Test removeAll of entire entry
254 allKeys.forEach(key -> {
255 map.removeAll(key).thenAccept(result -> {
256 assertTrue(
257 byteArrayCollectionIsEqual(allValues, result.value()));
258 }).join();
259 map.removeAll(key).thenAccept(result -> {
260 assertFalse(
261 byteArrayCollectionIsEqual(allValues, result.value()));
262 }).join();
263 });
264
265 map.isEmpty().thenAccept(result -> assertTrue(result)).join();
266
267 //Repopulate for next test
268 allKeys.forEach(key -> {
269 map.putAll(key, allValues)
270 .thenAccept(result -> assertTrue(result)).join();
271 });
272
273 map.size().thenAccept(result -> assertEquals(16, (int) result)).join();
274
275 allKeys.forEach(key -> {
276 map.replaceValues(key, allValues)
277 .thenAccept(result ->
278 assertTrue(byteArrayCollectionIsEqual(allValues,
279 result.value())))
280 .join();
281 map.replaceValues(key, Lists.newArrayList())
282 .thenAccept(result ->
283 assertTrue(byteArrayCollectionIsEqual(allValues,
284 result.value())))
285 .join();
286 map.replaceValues(key, allValues)
287 .thenAccept(result ->
288 assertTrue(result.value().isEmpty()))
289 .join();
290 });
291
292
293 //Test replacements of partial sets
294 map.size().thenAccept(result -> assertEquals(16, (int) result)).join();
295
296 allKeys.forEach(key -> {
297 map.remove(key, valueOne)
298 .thenAccept(result ->
299 assertTrue(result)).join();
300 map.replaceValues(key, Lists.newArrayList())
301 .thenAccept(result ->
302 assertTrue(byteArrayCollectionIsEqual(
303 Lists.newArrayList(valueTwo, valueThree,
304 valueFour),
305 result.value())))
306 .join();
307 map.replaceValues(key, allValues)
308 .thenAccept(result ->
309 assertTrue(result.value().isEmpty()))
310 .join();
311 });
312
313 map.destroy().join();
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -0700314 }
315
316 /**
317 * Tests the get, keySet, keys, values, and entries implementations as well
alshabibef10b732016-05-26 16:10:08 -0700318 * as a trivial test of the asMap functionality (throws error).
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -0700319 * @throws Exception
320 */
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -0700321 @Test
322 public void testAccessors() throws Exception {
Aaron Kruglikovb5a41e52016-06-23 15:37:41 -0700323 AsyncConsistentSetMultimap map = createResource("testFourMap");
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -0700324
325 //Populate for full map behavior tests
326 allKeys.forEach(key -> {
327 map.putAll(key, allValues)
328 .thenAccept(result -> assertTrue(result)).join();
329 });
330
331 map.size().thenAccept(result -> assertEquals(16, (int) result)).join();
332
333 allKeys.forEach(key -> {
334 map.get(key).thenAccept(result -> {
335 assertTrue(byteArrayCollectionIsEqual(allValues,
336 result.value()));
337 }).join();
338 });
339
340 //Test that the key set is correct
341 map.keySet()
342 .thenAccept(result ->
343 assertTrue(stringArrayCollectionIsEqual(allKeys,
344 result)))
345 .join();
346 //Test that the correct set and occurrence of values are found in the
347 //values result
348 map.values().thenAccept(result -> {
349 final Multiset<byte[]> set = TreeMultiset.create(
350 new ByteArrayComparator());
351 for (int i = 0; i < 4; i++) {
352 set.addAll(allValues);
353 }
354 assertEquals(16, result.size());
355 result.forEach(value -> assertTrue(set.remove(value)));
356 assertTrue(set.isEmpty());
357
358 }).join();
359
360 //Test that keys returns the right result including the correct number
361 //of each item
362 map.keys().thenAccept(result -> {
363 final Multiset<String> set = TreeMultiset.create();
364 for (int i = 0; i < 4; i++) {
365 set.addAll(allKeys);
366 }
367 assertEquals(16, result.size());
368 result.forEach(value -> assertTrue(set.remove(value)));
369 assertTrue(set.isEmpty());
370
371 }).join();
372
373 //Test that the right combination of key, value pairs are present
374 map.entries().thenAccept(result -> {
375 final Multiset<Map.Entry<String, byte[]>> set =
376 TreeMultiset.create(new EntryComparator());
377 allKeys.forEach(key -> {
378 allValues.forEach(value -> {
379 set.add(new DefaultMapEntry(key, value));
380 });
381 });
382 assertEquals(16, result.size());
383 result.forEach(entry -> assertTrue(set.remove(entry)));
384 assertTrue(set.isEmpty());
385 }).join();
386
387
388 //Testing for empty map behavior
389 map.clear().join();
390
391 allKeys.forEach(key -> {
392 map.get(key).thenAccept(result -> {
393 assertTrue(result.value().isEmpty());
394 }).join();
395 });
396
397 map.keySet().thenAccept(result -> assertTrue(result.isEmpty())).join();
398 map.values().thenAccept(result -> assertTrue(result.isEmpty())).join();
399 map.keys().thenAccept(result -> assertTrue(result.isEmpty())).join();
400 map.entries()
401 .thenAccept(result -> assertTrue(result.isEmpty())).join();
402
Aaron Kruglikovb5a41e52016-06-23 15:37:41 -0700403 map.destroy().join();
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -0700404 }
405
Aaron Kruglikovb5a41e52016-06-23 15:37:41 -0700406 private AsyncConsistentSetMultimap createResource(String mapName) {
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -0700407 try {
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -0700408 AsyncConsistentSetMultimap map = createAtomixClient().
Aaron Kruglikovb5a41e52016-06-23 15:37:41 -0700409 getResource("mapName", AsyncConsistentSetMultimap.class)
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -0700410 .join();
411 return map;
412 } catch (Throwable e) {
413 throw new RuntimeException(e.toString());
414 }
415 }
416
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -0700417 /**
418 * Returns two arrays contain the same set of elements,
419 * regardless of order.
420 * @param o1 first collection
421 * @param o2 second collection
422 * @return true if they contain the same elements
423 */
424 private boolean byteArrayCollectionIsEqual(
425 Collection<? extends byte[]> o1, Collection<? extends byte[]> o2) {
426 if (o1 == null || o2 == null || o1.size() != o2.size()) {
427 return false;
428 }
429 for (byte[] array1 : o1) {
430 boolean matched = false;
431 for (byte[] array2 : o2) {
432 if (Arrays.equals(array1, array2)) {
433 matched = true;
434 break;
435 }
436 }
437 if (!matched) {
438 return false;
439 }
440 }
441 return true;
442 }
443
444 /**
445 * Compares two collections of strings returns true if they contain the
446 * same strings, false otherwise.
447 * @param s1 string collection one
448 * @param s2 string collection two
449 * @return true if the two sets contain the same strings
450 */
451 private boolean stringArrayCollectionIsEqual(
452 Collection<? extends String> s1, Collection<? extends String> s2) {
453 if (s1 == null || s2 == null || s1.size() != s2.size()) {
454 return false;
455 }
456 for (String string1 : s1) {
457 boolean matched = false;
458 for (String string2 : s2) {
459 if (string1.equals(string2)) {
460 matched = true;
461 break;
462 }
463 }
464 if (!matched) {
465 return false;
466 }
467 }
468 return true;
469 }
470
471 /**
472 * Byte array comparator implementation.
473 */
474 private class ByteArrayComparator implements Comparator<byte[]> {
475
476 @Override
477 public int compare(byte[] o1, byte[] o2) {
478 if (Arrays.equals(o1, o2)) {
479 return 0;
480 } else {
481 for (int i = 0; i < o1.length && i < o2.length; i++) {
482 if (o1[i] < o2[i]) {
483 return -1;
484 } else if (o1[i] > o2[i]) {
485 return 1;
486 }
487 }
488 return o1.length > o2.length ? 1 : -1;
489 }
490 }
491 }
492
493 /**
494 * Entry comparator, uses both key and value to determine equality,
495 * for comparison falls back to the default string comparator.
496 */
497 private class EntryComparator
498 implements Comparator<Map.Entry<String, byte[]>> {
499
500 @Override
501 public int compare(Map.Entry<String, byte[]> o1,
502 Map.Entry<String, byte[]> o2) {
503 if (o1 == null || o1.getKey() == null || o2 == null ||
504 o2.getKey() == null) {
505 throw new IllegalArgumentException();
506 }
507 if (o1.getKey().equals(o2.getKey()) &&
508 Arrays.equals(o1.getValue(), o2.getValue())) {
509 return 0;
510 } else {
511 return o1.getKey().compareTo(o2.getKey());
512 }
513 }
514 }
515}