blob: bb7677eef4a9aa704e52854c59284a9de632f3e2 [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;
22import com.google.common.io.Files;
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -070023import io.atomix.resource.ResourceType;
24import org.apache.commons.collections.keyvalue.DefaultMapEntry;
Aaron Kruglikov5bb38122016-06-22 12:07:23 -070025import org.junit.Ignore;
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -070026import org.junit.Test;
27import org.onlab.util.Tools;
28
29import java.io.File;
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;
Aaron Kruglikovc0c27c02016-06-07 16:05:00 -070035import java.util.concurrent.atomic.AtomicInteger;
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -070036
37import static org.junit.Assert.assertEquals;
38import static org.junit.Assert.assertFalse;
39import static org.junit.Assert.assertTrue;
40
41/**
42 * Tests the {@link AsyncConsistentSetMultimap}.
43 */
Aaron Kruglikov5bb38122016-06-22 12:07:23 -070044@Ignore
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -070045public class AsyncConsistentSetMultimapTest extends AtomixTestBase {
46 private final File testDir = Files.createTempDir();
47 private final String keyOne = "hello";
48 private final String keyTwo = "goodbye";
49 private final String keyThree = "foo";
50 private final String keyFour = "bar";
51 private final byte[] valueOne = Tools.getBytesUtf8(keyOne);
52 private final byte[] valueTwo = Tools.getBytesUtf8(keyTwo);
53 private final byte[] valueThree = Tools.getBytesUtf8(keyThree);
54 private final byte[] valueFour = Tools.getBytesUtf8(keyFour);
55 private final List<String> allKeys = Lists.newArrayList(keyOne, keyTwo,
56 keyThree, keyFour);
57 private final List<byte[]> allValues = Lists.newArrayList(valueOne,
58 valueTwo,
59 valueThree,
60 valueFour);
Aaron Kruglikovc0c27c02016-06-07 16:05:00 -070061 private final AtomicInteger port = new AtomicInteger(49200);
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -070062
63 @Override
64 protected ResourceType resourceType() {
65 return new ResourceType(AsyncConsistentSetMultimap.class);
66 }
67
68 /**
69 * Test that size behaves correctly (This includes testing of the empty
70 * check).
71 */
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -070072 @Test
73 public void testSize() throws Throwable {
74 clearTests();
75 AsyncConsistentSetMultimap map = createResource(3);
76 //Simplest operation case
77 map.isEmpty().thenAccept(result -> assertTrue(result));
78 map.put(keyOne, valueOne).
79 thenAccept(result -> assertTrue(result)).join();
80 map.isEmpty().thenAccept(result -> assertFalse(result));
81 map.size().thenAccept(result -> assertEquals(1, (int) result))
82 .join();
83 //Make sure sizing is dependent on values not keys
84 map.put(keyOne, valueTwo).
85 thenAccept(result -> assertTrue(result)).join();
86 map.size().thenAccept(result -> assertEquals(2, (int) result))
87 .join();
88 //Ensure that double adding has no effect
89 map.put(keyOne, valueOne).
90 thenAccept(result -> assertFalse(result)).join();
91 map.size().thenAccept(result -> assertEquals(2, (int) result))
92 .join();
93 //Check handling for multiple keys
94 map.put(keyTwo, valueOne)
95 .thenAccept(result -> assertTrue(result)).join();
96 map.put(keyTwo, valueTwo)
97 .thenAccept(result -> assertTrue(result)).join();
98 map.size().thenAccept(result -> assertEquals(4, (int) result))
99 .join();
100 //Check size with removal
101 map.remove(keyOne, valueOne).
102 thenAccept(result -> assertTrue(result)).join();
103 map.size().thenAccept(result -> assertEquals(3, (int) result))
104 .join();
105 //Check behavior under remove of non-existant key
106 map.remove(keyOne, valueOne).
107 thenAccept(result -> assertFalse(result)).join();
108 map.size().thenAccept(result -> assertEquals(3, (int) result))
109 .join();
110 //Check clearing the entirety of the map
111 map.clear().join();
112 map.size().thenAccept(result -> assertEquals(0, (int) result))
113 .join();
114 map.isEmpty().thenAccept(result -> assertTrue(result));
115
116 map.destroy().join();
117 clearTests();
118 }
119
120 /**
121 * Contains tests for value, key and entry.
122 */
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -0700123 @Test
124 public void containsTest() throws Throwable {
125 clearTests();
126 AsyncConsistentSetMultimap map = createResource(3);
127
128 //Populate the maps
129 allKeys.forEach(key -> {
130 map.putAll(key, allValues)
131 .thenAccept(result -> assertTrue(result)).join();
132 });
133 map.size().thenAccept(result -> assertEquals(16, (int) result)).join();
134
135 //Test key contains positive results
136 allKeys.forEach(key -> {
137 map.containsKey(key)
138 .thenAccept(result -> assertTrue(result)).join();
139 });
140
141 //Test value contains positive results
142 allValues.forEach(value -> {
143 map.containsValue(value)
144 .thenAccept(result -> assertTrue(result)).join();
145 });
146
147 //Test contains entry for all possible entries
148 allKeys.forEach(key -> {
149 allValues.forEach(value -> {
150 map.containsEntry(key, value)
151 .thenAccept(result -> assertTrue(result)).join();
152 });
153 });
154
155 //Test behavior after removals
156 allValues.forEach(value -> {
157 final String[] removedKey = new String[1];
158 allKeys.forEach(key -> {
159 map.remove(key, value)
160 .thenAccept(result -> assertTrue(result)).join();
161 map.containsEntry(key, value)
162 .thenAccept(result -> assertFalse(result)).join();
163 removedKey[0] = key;
164 });
165 //Check that contains key works properly for removed keys
166 map.containsKey(removedKey[0])
167 .thenAccept(result -> assertFalse(result));
168 });
169
170 //Check that contains value works correctly for removed values
171 allValues.forEach(value -> {
172 map.containsValue(value)
173 .thenAccept(result -> assertFalse(result)).join();
174 });
175
176 map.destroy().join();
177 clearTests();
178 }
179
180 /**
181 * Contains tests for put, putAll, remove, removeAll and replace.
182 * @throws Exception
183 */
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -0700184 @Test
185 public void addAndRemoveTest() throws Exception {
186 clearTests();
187 AsyncConsistentSetMultimap map = createResource(3);
188
189 //Test single put
190 allKeys.forEach(key -> {
191 //Value should actually be added here
192 allValues.forEach(value -> {
193 map.put(key, value)
194 .thenAccept(result -> assertTrue(result)).join();
195 //Duplicate values should be ignored here
196 map.put(key, value)
197 .thenAccept(result -> assertFalse(result)).join();
198 });
199 });
200
201 //Test single remove
202 allKeys.forEach(key -> {
203 //Value should actually be added here
204 allValues.forEach(value -> {
205 map.remove(key, value)
206 .thenAccept(result -> assertTrue(result)).join();
207 //Duplicate values should be ignored here
208 map.remove(key, value)
209 .thenAccept(result -> assertFalse(result)).join();
210 });
211 });
212
213 map.isEmpty().thenAccept(result -> assertTrue(result)).join();
214
215 //Test multi put
216 allKeys.forEach(key -> {
217 map.putAll(key, Lists.newArrayList(allValues.subList(0, 2)))
218 .thenAccept(result -> assertTrue(result)).join();
219 map.putAll(key, Lists.newArrayList(allValues.subList(0, 2)))
220 .thenAccept(result -> assertFalse(result)).join();
221 map.putAll(key, Lists.newArrayList(allValues.subList(2, 4)))
222 .thenAccept(result -> assertTrue(result)).join();
223 map.putAll(key, Lists.newArrayList(allValues.subList(2, 4)))
224 .thenAccept(result -> assertFalse(result)).join();
225
226 });
227
228 //Test multi remove
229 allKeys.forEach(key -> {
230 //Split the lists to test how multiRemove can work piecewise
231 map.removeAll(key, Lists.newArrayList(allValues.subList(0, 2)))
232 .thenAccept(result -> assertTrue(result)).join();
233 map.removeAll(key, Lists.newArrayList(allValues.subList(0, 2)))
234 .thenAccept(result -> assertFalse(result)).join();
235 map.removeAll(key, Lists.newArrayList(allValues.subList(2, 4)))
236 .thenAccept(result -> assertTrue(result)).join();
237 map.removeAll(key, Lists.newArrayList(allValues.subList(2, 4)))
238 .thenAccept(result -> assertFalse(result)).join();
239 });
240
241 map.isEmpty().thenAccept(result -> assertTrue(result)).join();
242
243 //Repopulate for next test
244 allKeys.forEach(key -> {
245 map.putAll(key, allValues)
246 .thenAccept(result -> assertTrue(result)).join();
247 });
248
249 map.size().thenAccept(result -> assertEquals(16, (int) result)).join();
250
251 //Test removeAll of entire entry
252 allKeys.forEach(key -> {
253 map.removeAll(key).thenAccept(result -> {
254 assertTrue(
255 byteArrayCollectionIsEqual(allValues, result.value()));
256 }).join();
257 map.removeAll(key).thenAccept(result -> {
258 assertFalse(
259 byteArrayCollectionIsEqual(allValues, result.value()));
260 }).join();
261 });
262
263 map.isEmpty().thenAccept(result -> assertTrue(result)).join();
264
265 //Repopulate for next test
266 allKeys.forEach(key -> {
267 map.putAll(key, allValues)
268 .thenAccept(result -> assertTrue(result)).join();
269 });
270
271 map.size().thenAccept(result -> assertEquals(16, (int) result)).join();
272
273 allKeys.forEach(key -> {
274 map.replaceValues(key, allValues)
275 .thenAccept(result ->
276 assertTrue(byteArrayCollectionIsEqual(allValues,
277 result.value())))
278 .join();
279 map.replaceValues(key, Lists.newArrayList())
280 .thenAccept(result ->
281 assertTrue(byteArrayCollectionIsEqual(allValues,
282 result.value())))
283 .join();
284 map.replaceValues(key, allValues)
285 .thenAccept(result ->
286 assertTrue(result.value().isEmpty()))
287 .join();
288 });
289
290
291 //Test replacements of partial sets
292 map.size().thenAccept(result -> assertEquals(16, (int) result)).join();
293
294 allKeys.forEach(key -> {
295 map.remove(key, valueOne)
296 .thenAccept(result ->
297 assertTrue(result)).join();
298 map.replaceValues(key, Lists.newArrayList())
299 .thenAccept(result ->
300 assertTrue(byteArrayCollectionIsEqual(
301 Lists.newArrayList(valueTwo, valueThree,
302 valueFour),
303 result.value())))
304 .join();
305 map.replaceValues(key, allValues)
306 .thenAccept(result ->
307 assertTrue(result.value().isEmpty()))
308 .join();
309 });
310
311 map.destroy().join();
312 clearTests();
313 }
314
315 /**
316 * Tests the get, keySet, keys, values, and entries implementations as well
alshabibef10b732016-05-26 16:10:08 -0700317 * as a trivial test of the asMap functionality (throws error).
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -0700318 * @throws Exception
319 */
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -0700320 @Test
321 public void testAccessors() throws Exception {
322 clearTests();
323 AsyncConsistentSetMultimap map = createResource(3);
324
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
403 map.destroy();
404 clearTests();
405 }
406
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -0700407 private AsyncConsistentSetMultimap createResource(int clusterSize) {
408 try {
409 createCopycatServers(clusterSize);
410 AsyncConsistentSetMultimap map = createAtomixClient().
411 getResource("testMap", AsyncConsistentSetMultimap.class)
412 .join();
413 return map;
414 } catch (Throwable e) {
415 throw new RuntimeException(e.toString());
416 }
417 }
418
Aaron Kruglikov44a1fef2016-04-27 11:29:23 -0700419 /**
420 * Returns two arrays contain the same set of elements,
421 * regardless of order.
422 * @param o1 first collection
423 * @param o2 second collection
424 * @return true if they contain the same elements
425 */
426 private boolean byteArrayCollectionIsEqual(
427 Collection<? extends byte[]> o1, Collection<? extends byte[]> o2) {
428 if (o1 == null || o2 == null || o1.size() != o2.size()) {
429 return false;
430 }
431 for (byte[] array1 : o1) {
432 boolean matched = false;
433 for (byte[] array2 : o2) {
434 if (Arrays.equals(array1, array2)) {
435 matched = true;
436 break;
437 }
438 }
439 if (!matched) {
440 return false;
441 }
442 }
443 return true;
444 }
445
446 /**
447 * Compares two collections of strings returns true if they contain the
448 * same strings, false otherwise.
449 * @param s1 string collection one
450 * @param s2 string collection two
451 * @return true if the two sets contain the same strings
452 */
453 private boolean stringArrayCollectionIsEqual(
454 Collection<? extends String> s1, Collection<? extends String> s2) {
455 if (s1 == null || s2 == null || s1.size() != s2.size()) {
456 return false;
457 }
458 for (String string1 : s1) {
459 boolean matched = false;
460 for (String string2 : s2) {
461 if (string1.equals(string2)) {
462 matched = true;
463 break;
464 }
465 }
466 if (!matched) {
467 return false;
468 }
469 }
470 return true;
471 }
472
473 /**
474 * Byte array comparator implementation.
475 */
476 private class ByteArrayComparator implements Comparator<byte[]> {
477
478 @Override
479 public int compare(byte[] o1, byte[] o2) {
480 if (Arrays.equals(o1, o2)) {
481 return 0;
482 } else {
483 for (int i = 0; i < o1.length && i < o2.length; i++) {
484 if (o1[i] < o2[i]) {
485 return -1;
486 } else if (o1[i] > o2[i]) {
487 return 1;
488 }
489 }
490 return o1.length > o2.length ? 1 : -1;
491 }
492 }
493 }
494
495 /**
496 * Entry comparator, uses both key and value to determine equality,
497 * for comparison falls back to the default string comparator.
498 */
499 private class EntryComparator
500 implements Comparator<Map.Entry<String, byte[]>> {
501
502 @Override
503 public int compare(Map.Entry<String, byte[]> o1,
504 Map.Entry<String, byte[]> o2) {
505 if (o1 == null || o1.getKey() == null || o2 == null ||
506 o2.getKey() == null) {
507 throw new IllegalArgumentException();
508 }
509 if (o1.getKey().equals(o2.getKey()) &&
510 Arrays.equals(o1.getValue(), o2.getValue())) {
511 return 0;
512 } else {
513 return o1.getKey().compareTo(o2.getKey());
514 }
515 }
516 }
517}