1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.atlassian.util.concurrent;
18
19 import static com.atlassian.util.concurrent.TestUtil.serialize;
20 import static java.util.Arrays.asList;
21 import static org.junit.Assert.assertArrayEquals;
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertFalse;
24 import static org.junit.Assert.assertNotNull;
25 import static org.junit.Assert.assertNull;
26 import static org.junit.Assert.assertTrue;
27 import static org.junit.Assert.fail;
28
29 import com.atlassian.util.concurrent.AbstractCopyOnWriteMap.View;
30 import com.atlassian.util.concurrent.MapBuilder.E;
31
32 import org.junit.Test;
33
34 import java.util.ArrayList;
35 import java.util.Collection;
36 import java.util.Collections;
37 import java.util.HashMap;
38 import java.util.Iterator;
39 import java.util.Map;
40 import java.util.Map.Entry;
41 import java.util.concurrent.ConcurrentMap;
42 import java.util.concurrent.atomic.AtomicInteger;
43 import java.util.concurrent.atomic.AtomicReference;
44
45 public class CopyOnWriteMapTest {
46
47 @Test
48 public void factoryCalledOnConstructor() {
49 final AtomicInteger count = new AtomicInteger();
50 final Map<String, String> init = MapBuilder.build("1", "o1", "2", "o2", "3", "o3");
51 final Map<String, String> map = new CopyOnWriteMap<String, String>(init, View.Type.STABLE) {
52 private static final long serialVersionUID = 8866224559807093002L;
53
54 @Override
55 public <N extends Map<? extends String, ? extends String>> Map<String, String> copy(final N map) {
56 count.getAndIncrement();
57 return new HashMap<String, String>(map);
58 }
59 };
60 assertEquals(1, count.get());
61 assertEquals(3, map.size());
62 assertTrue(map.containsKey("2"));
63 assertTrue(map.containsValue("o3"));
64 assertEquals("o1", map.get("1"));
65 }
66
67 @Test
68 public void factoryCalledOnWrite() {
69 final AtomicInteger count = new AtomicInteger();
70 final Map<String, String> map = new CopyOnWriteMap<String, String>(View.Type.STABLE) {
71 private static final long serialVersionUID = -3858713272422952372L;
72
73 @Override
74 public <N extends Map<? extends String, ? extends String>> Map<String, String> copy(final N map) {
75 count.getAndIncrement();
76 return new HashMap<String, String>(map);
77 }
78 };
79
80 assertEquals("should be called in ctor", 1, count.get());
81 map.put("test", "test");
82 assertEquals("should be called in put", 2, count.get());
83 assertEquals(1, map.size());
84 assertTrue(map.containsKey("test"));
85 assertTrue(map.containsValue("test"));
86 assertEquals("should not be called in reads", 2, count.get());
87 map.putAll(MapBuilder.build("1", "test1", "2", "test2", "3", "test3"));
88 assertEquals("should be called in putAll", 3, count.get());
89 assertEquals(4, map.size());
90 assertTrue(map.containsKey("1"));
91 assertTrue(map.containsValue("test3"));
92 map.remove("2");
93 assertEquals("should be called in remove", 4, count.get());
94 assertEquals(3, map.size());
95 assertFalse(map.containsValue("test2"));
96 map.clear();
97 assertEquals("should be called in clear", 5, count.get());
98 assertEquals(0, map.size());
99 }
100
101 @Test
102 public void delegateHashMap() throws Exception {
103 final Map<String, String> map = MapBuilder.<String, String> builder().add("key", "value").toMap();
104 final CopyOnWriteMap<String, String> cowMap = CopyOnWriteMap.<String, String> builder().addAll(map).newHashMap();
105 assertEquals(map, cowMap);
106 assertEquals(map.hashCode(), cowMap.hashCode());
107 assertEquals(map.toString(), cowMap.toString());
108 }
109
110 @Test
111 public void delegateKeySet() throws Exception {
112 final Map<String, String> map = MapBuilder.<String, String> builder().add("key", "value").toMap();
113 final CopyOnWriteMap<String, String> cowMap = CopyOnWriteMap.<String, String> builder().addAll(map).newHashMap();
114 assertEquals(map.keySet(), cowMap.keySet());
115 assertEquals(map.keySet().hashCode(), cowMap.keySet().hashCode());
116 assertEquals(map.keySet().toString(), cowMap.keySet().toString());
117 }
118
119 @Test
120 public void delegateEqualityValues() throws Exception {
121 final Map<String, String> map = MapBuilder.<String, String> builder().add("key", "value").toMap();
122 final CopyOnWriteMap<String, String> cowMap = CopyOnWriteMap.<String, String> builder().addAll(map).newHashMap();
123 assertEquals(new ArrayList<String>(map.values()), new ArrayList<String>(cowMap.values()));
124 assertEquals(new ArrayList<String>(map.values()).hashCode(), new ArrayList<String>(cowMap.values()).hashCode());
125 assertEquals(map.values().toString(), cowMap.values().toString());
126 }
127
128 @Test
129 public void delegateEntrySet() throws Exception {
130 final Map<String, String> map = MapBuilder.<String, String> builder().add("key", "value").toMap();
131 final CopyOnWriteMap<String, String> cowMap = CopyOnWriteMap.<String, String> builder().addAll(map).newHashMap();
132 assertEquals(map.entrySet(), cowMap.entrySet());
133 assertEquals(map.entrySet().hashCode(), cowMap.entrySet().hashCode());
134 assertEquals(map.entrySet().toString(), cowMap.entrySet().toString());
135 }
136
137 @Test
138 public void delegateLinked() throws Exception {
139 final Map<String, String> map = MapBuilder.<String, String> builder().add("key", "value").toMap();
140 final CopyOnWriteMap<String, String> cowMap = CopyOnWriteMap.<String, String> builder().addAll(map).newLinkedMap();
141 assertEquals(map, cowMap);
142 assertEquals(map.hashCode(), cowMap.hashCode());
143 assertEquals(map.toString(), cowMap.toString());
144 }
145
146 @Test
147 public void delegateKeySetLinked() throws Exception {
148 final Map<String, String> map = MapBuilder.<String, String> builder().add("key", "value").toMap();
149 final CopyOnWriteMap<String, String> cowMap = CopyOnWriteMap.<String, String> builder().addAll(map).newLinkedMap();
150 assertEquals(map.keySet(), cowMap.keySet());
151 assertEquals(map.keySet().hashCode(), cowMap.keySet().hashCode());
152 assertEquals(map.keySet().toString(), cowMap.keySet().toString());
153 }
154
155 @Test
156 public void delegateValuesLinked() throws Exception {
157 final Map<String, String> map = MapBuilder.<String, String> builder().add("key", "value").toMap();
158 final CopyOnWriteMap<String, String> cowMap = CopyOnWriteMap.<String, String> builder().addAll(map).newLinkedMap();
159 assertEquals(new ArrayList<String>(map.values()), new ArrayList<String>(cowMap.values()));
160 assertEquals(new ArrayList<String>(map.values()).hashCode(), new ArrayList<String>(cowMap.values()).hashCode());
161 assertEquals(map.values().toString(), cowMap.values().toString());
162 }
163
164 @Test
165 public void delegateEntrySetLinked() throws Exception {
166 final Map<String, String> map = MapBuilder.<String, String> builder().add("key", "value").toMap();
167 final CopyOnWriteMap<String, String> cowMap = CopyOnWriteMap.<String, String> builder().addAll(map).newLinkedMap();
168 assertEquals(map.entrySet(), cowMap.entrySet());
169 assertEquals(map.entrySet().hashCode(), cowMap.entrySet().hashCode());
170 assertEquals(map.entrySet().toString(), cowMap.entrySet().toString());
171 }
172
173 @Test
174 public void putIfAbsentWorksIfAbsent() throws Exception {
175 final Map<String, String> init = MapBuilder.<String, String> builder().add("key", "value").toMap();
176 final ConcurrentMap<String, String> map = CopyOnWriteMap.<String, String> builder().addAll(init).newHashMap();
177 assertNull(map.putIfAbsent("key2", "value2"));
178 assertEquals(2, map.size());
179 assertTrue(map.containsKey("key2"));
180 assertTrue(map.containsValue("value2"));
181 }
182
183 @Test
184 public void putIfAbsentFailsIfPresent() throws Exception {
185 final Map<String, String> init = MapBuilder.<String, String> builder().add("key", "value").add("key2", "value2").toMap();
186 final ConcurrentMap<String, String> map = CopyOnWriteMap.<String, String> builder().addAll(init).newHashMap();
187 map.putIfAbsent("key2", "value3");
188 assertNotNull(map.putIfAbsent("key2", "value2"));
189 assertEquals(2, map.size());
190 assertTrue(map.containsKey("key2"));
191 assertTrue(map.containsValue("value2"));
192 assertFalse(map.containsValue("value3"));
193 }
194
195 @Test
196 public void removeWorksIfValueSame() throws Exception {
197 final Map<String, String> init = MapBuilder.<String, String> builder().add("key", "value").add("key2", "value2").toMap();
198 final ConcurrentMap<String, String> map = CopyOnWriteMap.<String, String> builder().addAll(init).newHashMap();
199 assertTrue(map.remove("key2", "value2"));
200 assertEquals(1, map.size());
201 assertFalse(map.containsKey("key2"));
202 assertFalse(map.containsValue("value2"));
203 }
204
205 @Test
206 public void removeFailsIfValueDifferent() throws Exception {
207 final Map<String, String> init = MapBuilder.<String, String> builder().add("key", "value").add("key2", "value2").toMap();
208 final ConcurrentMap<String, String> map = CopyOnWriteMap.<String, String> builder().addAll(init).newHashMap();
209 assertFalse(map.remove("key2", "value3"));
210 assertEquals(2, map.size());
211 assertTrue(map.containsKey("key2"));
212 assertTrue(map.containsValue("value2"));
213 assertFalse(map.containsValue("value3"));
214 }
215
216 @Test
217 public void unconditionalReplaceWorksIfKeyMapped() throws Exception {
218 final Map<String, String> init = MapBuilder.<String, String> builder().add("key", "value").add("key2", "value2").toMap();
219 final ConcurrentMap<String, String> map = CopyOnWriteMap.<String, String> builder().addAll(init).newHashMap();
220 assertNotNull(map.replace("key2", "value3"));
221 assertEquals(2, map.size());
222 assertTrue(map.containsKey("key2"));
223 assertTrue(map.containsValue("value3"));
224 assertFalse(map.containsValue("value2"));
225 }
226
227 @Test
228 public void unconditionalReplaceFailsIfKeyMissing() throws Exception {
229 final Map<String, String> init = MapBuilder.<String, String> builder().add("key", "value").add("key2", "value2").toMap();
230 final ConcurrentMap<String, String> map = CopyOnWriteMap.<String, String> builder().addAll(init).newHashMap();
231 assertNull(map.replace("key3", "value3"));
232 assertEquals(2, map.size());
233 assertTrue(map.containsKey("key2"));
234 assertTrue(map.containsValue("value2"));
235 assertFalse(map.containsKey("key3"));
236 assertFalse(map.containsValue("value3"));
237 }
238
239 @Test
240 public void conditionalReplaceWorksIfKeyAndValueMapped() throws Exception {
241 final Map<String, String> init = MapBuilder.<String, String> builder().add("key", "value").add("key2", "value2").toMap();
242 final ConcurrentMap<String, String> map = CopyOnWriteMap.<String, String> builder().addAll(init).newHashMap();
243 assertTrue(map.replace("key2", "value2", "value3"));
244 assertEquals(2, map.size());
245 assertTrue(map.containsKey("key2"));
246 assertTrue(map.containsValue("value3"));
247 assertFalse(map.containsValue("value2"));
248 }
249
250 @Test
251 public void conditionalReplaceWorksIfValueNull() throws Exception {
252 final Map<String, String> init = MapBuilder.<String, String> builder().add("key", "value").add("key2", null).toMap();
253 final ConcurrentMap<String, String> map = CopyOnWriteMap.<String, String> builder().addAll(init).newHashMap();
254 assertTrue(map.replace("key2", null, "value3"));
255 assertEquals(2, map.size());
256 assertTrue(map.containsKey("key2"));
257 assertTrue(map.containsValue("value3"));
258 assertFalse(map.containsValue(null));
259 }
260
261 @Test
262 public void conditionalReplaceFailsIfValueDifferent() throws Exception {
263 final Map<String, String> init = MapBuilder.<String, String> builder().add("key", "value").add("key2", "value2").toMap();
264 final ConcurrentMap<String, String> map = CopyOnWriteMap.<String, String> builder().addAll(init).newHashMap();
265 assertFalse(map.replace("key2", "value3", "value4"));
266 assertEquals(2, map.size());
267 assertTrue(map.containsKey("key2"));
268 assertTrue(map.containsValue("value2"));
269 assertFalse(map.containsValue("value3"));
270 assertFalse(map.containsValue("value4"));
271 }
272
273 @Test
274 public void conditionalReplaceFailsIfKeyMissing() throws Exception {
275 final Map<String, String> init = MapBuilder.<String, String> builder().add("key", "value").add("key2", "value2").toMap();
276 final ConcurrentMap<String, String> map = CopyOnWriteMap.<String, String> builder().addAll(init).newHashMap();
277 assertFalse(map.replace("key3", "value2", "value3"));
278 assertEquals(2, map.size());
279 assertTrue(map.containsKey("key2"));
280 assertTrue(map.containsValue("value2"));
281 assertFalse(map.containsKey("key3"));
282 assertFalse(map.containsValue("value3"));
283 }
284
285 @Test
286 public void modifiableValues() throws Exception {
287 final AtomicInteger count = new AtomicInteger();
288 final Map<String, String> init = new MapBuilder<String, String>().add("test", "test").add("testing", "testing").add("sup", "tester").toMap();
289 final Map<String, String> map = new CopyOnWriteMap<String, String>(init, View.Type.LIVE) {
290 private static final long serialVersionUID = 3275978982528321604L;
291
292 @Override
293 public <N extends Map<? extends String, ? extends String>> Map<String, String> copy(final N map) {
294 count.getAndIncrement();
295 return new HashMap<String, String>(map);
296 }
297 };
298 assertEquals(1, count.get());
299 final Collection<String> values = map.values();
300 try {
301 values.add("something");
302 fail("UnsupportedOp expected");
303 } catch (final UnsupportedOperationException ignore) {}
304 assertEquals(1, count.get());
305 try {
306 values.addAll(asList("one", "two", "three"));
307 fail("UnsupportedOp expected");
308 } catch (final UnsupportedOperationException ignore) {}
309 final Iterator<String> iterator = values.iterator();
310 assertTrue(iterator.hasNext());
311 assertNotNull(iterator.next());
312 try {
313 iterator.remove();
314 fail("UnsupportedOp expected");
315 } catch (final UnsupportedOperationException ignore) {}
316 assertEquals(1, count.get());
317 assertFalse(values.remove("blah"));
318 assertEquals("not modified if element not present to be removed", 1, count.get());
319 assertTrue(values.remove("test"));
320 assertEquals(2, count.get());
321 assertEquals(2, map.size());
322 assertFalse(values.retainAll(asList("testing", "tester")));
323 assertEquals(3, count.get());
324 assertEquals(2, map.size());
325 assertTrue(values.removeAll(asList("test", "testing")));
326 assertEquals(4, count.get());
327 assertEquals(1, map.size());
328 values.clear();
329 assertTrue(map.isEmpty());
330 }
331
332 @Test
333 public void modifiableEntrySet() throws Exception {
334 final AtomicInteger count = new AtomicInteger();
335 final Map<String, String> init = new MapBuilder<String, String>().add("test", "test").add("testing", "testing").add("tester", "tester")
336 .toMap();
337 final Map<String, String> map = new CopyOnWriteMap<String, String>(init, View.Type.LIVE) {
338 private static final long serialVersionUID = -2882860445706454721L;
339
340 @Override
341 public <N extends Map<? extends String, ? extends String>> Map<String, String> copy(final N map) {
342 count.getAndIncrement();
343 return new HashMap<String, String>(map);
344 }
345 };
346 assertEquals(1, count.get());
347 final Collection<Entry<String, String>> entries = map.entrySet();
348
349 try {
350 entries.add(new E("something"));
351 fail("UnsupportedOp expected");
352 } catch (final UnsupportedOperationException ignore) {}
353 assertEquals(1, count.get());
354 try {
355 entries.addAll(asList(new E("one"), new E("two"), new E("three")));
356 fail("UnsupportedOp expected");
357 } catch (final UnsupportedOperationException ignore) {}
358 final Iterator<Entry<String, String>> iterator = entries.iterator();
359 assertTrue(iterator.hasNext());
360 assertNotNull(iterator.next());
361 try {
362 iterator.remove();
363 fail("UnsupportedOp expected");
364 } catch (final UnsupportedOperationException ignore) {}
365 assertEquals(1, count.get());
366 assertFalse(entries.remove("blah"));
367 assertEquals("not modified if element not present to be removed", 1, count.get());
368 assertTrue(entries.remove(new E("test")));
369 assertEquals(2, count.get());
370 assertEquals(2, map.size());
371 assertFalse(entries.retainAll(asList(new E("testing"), new E("tester"))));
372 assertEquals(3, count.get());
373 assertEquals(2, map.size());
374 assertTrue(entries.removeAll(asList(new E("test"), new E("testing"))));
375 assertEquals(4, count.get());
376 assertEquals(1, map.size());
377 entries.clear();
378 assertTrue(map.isEmpty());
379 map.put("key", "key");
380 assertTrue(entries.contains(new E("key")));
381 }
382
383 @Test
384 public void modifiableKeySet() throws Exception {
385 final AtomicInteger count = new AtomicInteger();
386
387 final Map<String, String> init = new MapBuilder<String, String>().add("test", "test").add("testing", "testing").add("tester", "tester")
388 .toMap();
389 final Map<String, String> map = new CopyOnWriteMap<String, String>(init, View.Type.LIVE) {
390 private static final long serialVersionUID = 7273654247572679525L;
391
392 @Override
393 public <N extends Map<? extends String, ? extends String>> Map<String, String> copy(final N map) {
394 count.getAndIncrement();
395 return new HashMap<String, String>(map);
396 }
397 };
398 assertEquals(1, count.get());
399 final Collection<String> keys = map.keySet();
400 try {
401 keys.add("something");
402 fail("UnsupportedOp expected");
403 } catch (final UnsupportedOperationException ignore) {}
404 assertEquals(1, count.get());
405 try {
406 keys.addAll(asList("one", "two", "three"));
407 fail("UnsupportedOp expected");
408 } catch (final UnsupportedOperationException ignore) {}
409 final Iterator<String> iterator = keys.iterator();
410 assertTrue(iterator.hasNext());
411 assertNotNull(iterator.next());
412 try {
413 iterator.remove();
414 fail("UnsupportedOp expected");
415 } catch (final UnsupportedOperationException ignore) {}
416 assertEquals(1, count.get());
417 assertFalse(keys.remove("blah"));
418 assertEquals("not modified if element not present to be removed", 1, count.get());
419 assertTrue(keys.remove("test"));
420 assertEquals(2, count.get());
421 assertEquals(2, map.size());
422 assertFalse(keys.retainAll(asList("testing", "tester")));
423 assertEquals(3, count.get());
424 assertEquals(2, map.size());
425 assertTrue(keys.removeAll(asList("test", "testing")));
426 assertEquals(4, count.get());
427 assertEquals(1, map.size());
428 keys.clear();
429 assertTrue(map.isEmpty());
430 map.put("key", "key");
431 assertTrue(keys.contains("key"));
432 }
433
434 @Test
435 public void unmodifiableValues() throws Exception {
436 final AtomicInteger count = new AtomicInteger();
437 final Map<String, String> init = new MapBuilder<String, String>().add("test", "test").add("testing", "testing").add("sup", "tester").toMap();
438 final Map<String, String> map = new CopyOnWriteMap<String, String>(init, View.Type.STABLE) {
439 private static final long serialVersionUID = 3275978982528321604L;
440
441 @Override
442 public <N extends Map<? extends String, ? extends String>> Map<String, String> copy(final N map) {
443 count.getAndIncrement();
444 return new HashMap<String, String>(map);
445 }
446 };
447 assertEquals(1, count.get());
448 final Collection<String> values = map.values();
449 try {
450 values.add("something");
451 fail("UnsupportedOp expected");
452 } catch (final UnsupportedOperationException ignore) {}
453 assertEquals(1, count.get());
454 try {
455 values.addAll(asList("one", "two", "three"));
456 fail("UnsupportedOp expected");
457 } catch (final UnsupportedOperationException ignore) {}
458 final Iterator<String> iterator = values.iterator();
459 assertTrue(iterator.hasNext());
460 assertNotNull(iterator.next());
461 try {
462 iterator.remove();
463 fail("UnsupportedOp expected");
464 } catch (final UnsupportedOperationException ignore) {}
465 assertEquals(1, count.get());
466 try {
467 values.remove("blah");
468 fail("UnsupportedOp expected");
469 } catch (final UnsupportedOperationException ignore) {}
470 try {
471 values.retainAll(asList("testing", "tester"));
472 fail("UnsupportedOp expected");
473 } catch (final UnsupportedOperationException ignore) {}
474 try {
475 values.removeAll(asList("test", "testing"));
476 fail("UnsupportedOp expected");
477 } catch (final UnsupportedOperationException ignore) {}
478 try {
479 values.clear();
480 fail("UnsupportedOp expected");
481 } catch (final UnsupportedOperationException ignore) {}
482 map.put("lalala", "lalala");
483 assertFalse(values.contains("lalala"));
484 CopyOnWriteSortedMapTest.assertUnmodifiableCollection(values, "lalala");
485 }
486
487 @Test
488 public void unmodifiableEntrySet() throws Exception {
489 final AtomicInteger count = new AtomicInteger();
490 final Map<String, String> init = new MapBuilder<String, String>().add("test", "test").add("testing", "testing").add("tester", "tester")
491 .toMap();
492 final Map<String, String> map = new CopyOnWriteMap<String, String>(init, View.Type.STABLE) {
493 private static final long serialVersionUID = -2882860445706454721L;
494
495 @Override
496 public <N extends Map<? extends String, ? extends String>> Map<String, String> copy(final N map) {
497 count.getAndIncrement();
498 return new HashMap<String, String>(map);
499 }
500 };
501 assertEquals(1, count.get());
502 final Collection<Entry<String, String>> entries = map.entrySet();
503
504 try {
505 entries.add(new E("something"));
506 fail("UnsupportedOp expected");
507 } catch (final UnsupportedOperationException ignore) {}
508 assertEquals(1, count.get());
509 try {
510 entries.addAll(asList(new E("one"), new E("two"), new E("three")));
511 fail("UnsupportedOp expected");
512 } catch (final UnsupportedOperationException ignore) {}
513 final Iterator<Entry<String, String>> iterator = entries.iterator();
514 assertTrue(iterator.hasNext());
515 assertNotNull(iterator.next());
516 try {
517 iterator.remove();
518 fail("UnsupportedOp expected");
519 } catch (final UnsupportedOperationException ignore) {}
520 assertEquals(1, count.get());
521 try {
522 assertFalse(entries.remove("blah"));
523 fail("UnsupportedOp expected");
524 } catch (final UnsupportedOperationException ignore) {}
525 try {
526 entries.retainAll(asList(new E("testing"), new E("tester")));
527 fail("UnsupportedOp expected");
528 } catch (final UnsupportedOperationException ignore) {}
529 try {
530 entries.removeAll(asList(new E("test"), new E("testing")));
531 fail("UnsupportedOp expected");
532 } catch (final UnsupportedOperationException ignore) {}
533 try {
534 entries.clear();
535 fail("UnsupportedOp expected");
536 } catch (final UnsupportedOperationException ignore) {}
537 map.put("key", "key");
538 assertFalse(entries.contains(new E("key")));
539 CopyOnWriteSortedMapTest.assertUnmodifiableCollection(entries, new E("lalala"));
540 }
541
542 @Test
543 public void unmodifiableKeySet() throws Exception {
544 final AtomicInteger count = new AtomicInteger();
545
546 final Map<String, String> init = new MapBuilder<String, String>().add("test", "test").add("testing", "testing").add("tester", "tester")
547 .toMap();
548 final Map<String, String> map = new CopyOnWriteMap<String, String>(init, View.Type.STABLE) {
549 private static final long serialVersionUID = 7273654247572679525L;
550
551 @Override
552 public <N extends Map<? extends String, ? extends String>> Map<String, String> copy(final N map) {
553 count.getAndIncrement();
554 return new HashMap<String, String>(map);
555 }
556 };
557 assertEquals(1, count.get());
558 final Collection<String> keys = map.keySet();
559 try {
560 keys.add("something");
561 fail("UnsupportedOp expected");
562 } catch (final UnsupportedOperationException ignore) {}
563 assertEquals(1, count.get());
564 try {
565 keys.addAll(asList("one", "two", "three"));
566 fail("UnsupportedOp expected");
567 } catch (final UnsupportedOperationException ignore) {}
568 final Iterator<String> iterator = keys.iterator();
569 assertTrue(iterator.hasNext());
570 assertNotNull(iterator.next());
571 try {
572 iterator.remove();
573 fail("UnsupportedOp expected");
574 } catch (final UnsupportedOperationException ignore) {}
575 assertEquals(1, count.get());
576 try {
577 keys.remove("blah");
578 fail("UnsupportedOp expected");
579 } catch (final UnsupportedOperationException ignore) {}
580 try {
581 keys.remove("test");
582 fail("UnsupportedOp expected");
583 } catch (final UnsupportedOperationException ignore) {}
584 try {
585 keys.retainAll(asList("testing", "tester"));
586 fail("UnsupportedOp expected");
587 } catch (final UnsupportedOperationException ignore) {}
588 try {
589 keys.removeAll(asList("test", "testing"));
590 fail("UnsupportedOp expected");
591 } catch (final UnsupportedOperationException ignore) {}
592 try {
593 keys.clear();
594 fail("UnsupportedOp expected");
595 } catch (final UnsupportedOperationException ignore) {}
596 map.put("key", "lala");
597 assertFalse(keys.contains("key"));
598 CopyOnWriteSortedMapTest.assertUnmodifiableCollection(keys, "key");
599 }
600
601 @Test(expected = IllegalArgumentException.class)
602 public void nullMap() throws Exception {
603 new CopyOnWriteMap<String, String>(null, View.Type.STABLE) {
604 private static final long serialVersionUID = 4223850632932526917L;
605
606
607 @Override
608 public <N extends Map<? extends String, ? extends String>> Map<String, String> copy(final N map) {
609 return new HashMap<String, String>(map);
610 };
611
612 };
613 }
614
615 @Test(expected = IllegalArgumentException.class)
616 public void nullViewType() throws Exception {
617 new CopyOnWriteMap<String, String>(new HashMap<String, String>(), null) {
618 private static final long serialVersionUID = 4223850632932526917L;
619
620
621 @Override
622 public <N extends Map<? extends String, ? extends String>> Map<String, String> copy(final N map) {
623 return new HashMap<String, String>(map);
624 };
625
626 };
627 }
628
629 @Test(expected = IllegalArgumentException.class)
630 public void copyFunctionReturnsNull() throws Exception {
631 new CopyOnWriteMap<String, String>(View.Type.STABLE) {
632 private static final long serialVersionUID = 831716474176011289L;
633
634 @Override
635 public <N extends Map<? extends String, ? extends String>> Map<String, String> copy(final N map) {
636 return null;
637 };
638 };
639 }
640
641 @Test
642 public void serializableHashMap() {
643 assertMutableMapSerializable(CopyOnWriteMap.builder().newHashMap());
644 }
645
646 @Test
647 public void serializableLinkedMap() {
648 assertMutableMapSerializable(CopyOnWriteMap.builder().newLinkedMap());
649 }
650
651 @Test
652 public void toStringTest() throws Exception {
653 final AtomicReference<Map<String, String>> ref = new AtomicReference<Map<String, String>>();
654 final CopyOnWriteMap<String, String> cowMap = new CopyOnWriteMap<String, String>(View.Type.STABLE) {
655 private static final long serialVersionUID = -17380087385174856L;
656
657 @Override
658 protected <N extends Map<? extends String, ? extends String>> java.util.Map<String, String> copy(final N map) {
659 ref.set(new HashMap<String, String>(map));
660 return ref.get();
661 };
662 };
663 assertEquals(ref.get().toString(), cowMap.toString());
664 }
665
666 @Test
667 public void isEmpty() throws Exception {
668 final CopyOnWriteMap<String, String> cowMap = CopyOnWriteMap.<String, String> builder().newHashMap();
669 assertTrue(cowMap.isEmpty());
670 assertTrue(cowMap.keySet().isEmpty());
671 assertTrue(cowMap.entrySet().isEmpty());
672 assertTrue(cowMap.values().isEmpty());
673 cowMap.put("1", "1");
674 assertFalse(cowMap.isEmpty());
675 assertFalse(cowMap.keySet().isEmpty());
676 assertFalse(cowMap.entrySet().isEmpty());
677 assertFalse(cowMap.values().isEmpty());
678 }
679
680 @Test
681 public void equality() throws Exception {
682 final Map<String, String> init = new MapBuilder<String, String>().add("test", "test").add("testing", "testing").toMap();
683 final CopyOnWriteMap<String, String> map = CopyOnWriteMap.<String, String> builder().addAll(init).newHashMap();
684 assertEquals(init, map);
685 assertEquals(map, init);
686 assertEquals(init.hashCode(), map.hashCode());
687 assertEquals(map.hashCode(), init.hashCode());
688 assertEquals(init.keySet(), map.keySet());
689 assertEquals(map.keySet(), init.keySet());
690 assertEquals(init.entrySet(), map.entrySet());
691 assertEquals(map.entrySet(), init.entrySet());
692 assertFalse(init.values().equals(map.values()));
693 assertFalse(map.values().equals(init.values()));
694 }
695
696 @Test
697 public void toArray() throws Exception {
698 final Map<String, String> init = new MapBuilder<String, String>().add("test", "test").add("testing", "testing").toMap();
699 final CopyOnWriteMap<String, String> map = CopyOnWriteMap.<String, String> builder().addAll(init).newHashMap();
700 assertArrayEquals(init.keySet().toArray(new String[2]), map.keySet().toArray(new String[2]));
701 assertArrayEquals(init.values().toArray(new String[2]), map.values().toArray(new String[2]));
702 assertArrayEquals(init.entrySet().toArray(new Map.Entry[2]), map.entrySet().toArray(new Map.Entry[2]));
703 }
704
705 @Test
706 public void contains() throws Exception {
707 final Map<String, String> init = MapBuilder.build("1", "o1", "2", "o2", "3", "o3");
708 final Map<String, String> map = CopyOnWriteMap.<String, String> builder().addAll(init).newHashMap();
709 assertTrue(map.containsKey("2"));
710 assertTrue(map.containsValue("o2"));
711 assertTrue(map.keySet().contains("2"));
712 assertTrue(map.keySet().containsAll(asList(new String[] { "1", "2", "3" })));
713 assertTrue(map.values().contains("o2"));
714 assertTrue(map.values().containsAll(asList(new String[] { "o1", "o2", "o3" })));
715 }
716
717 static void assertMutableMapSerializable(Map<Object, Object> map) {
718 map.put("1", "one");
719 map = assertSerializable(map);
720 assertTrue(map.containsKey("1"));
721 assertTrue(map.containsValue("one"));
722 assertEquals("1", map.keySet().iterator().next());
723 assertEquals("one", map.values().iterator().next());
724 final Map.Entry<Object, Object> entry = map.entrySet().iterator().next();
725 assertEquals("1", entry.getKey());
726 assertEquals("one", entry.getValue());
727 }
728
729 static Map<Object, Object> assertSerializable(final Map<Object, Object> map) {
730 final Map<Object, Object> result = serialize(map);
731 assertEquals(map, result);
732 return result;
733 }
734 }
735
736 class MapBuilder<K, V> {
737 private final Map<K, V> map = new HashMap<K, V>();
738
739 static <S> Map<S, S> build(final S... elements) {
740 if (elements.length % 2 != 0) {
741 throw new IllegalArgumentException("must have even number of elements: " + elements.length);
742 }
743 final MapBuilder<S, S> result = new MapBuilder<S, S>();
744 for (int i = 0; i < elements.length; i = i + 2) {
745 result.add(elements[i], elements[i + 1]);
746 }
747 return result.toMap();
748 }
749
750 static <K, V> MapBuilder<K, V> builder() {
751 return new MapBuilder<K, V>();
752 }
753
754 MapBuilder<K, V> add(final K key, final V value) {
755 map.put(key, value);
756 return this;
757 }
758
759 Entry<K, V> entry(final K key, final V value) {
760 return new Entry<K, V>() {
761 public K getKey() {
762 return key;
763 }
764
765 public V getValue() {
766 return value;
767 }
768
769 public V setValue(final V arg0) {
770 throw new UnsupportedOperationException();
771 };
772 };
773 }
774
775 Map<K, V> toMap() {
776 return Collections.unmodifiableMap(new HashMap<K, V>(map));
777 }
778
779 static class E implements Map.Entry<String, String> {
780 final String e;
781
782 public E(final String e) {
783 this.e = e;
784 }
785
786 public String getKey() {
787 return e;
788 }
789
790 public String getValue() {
791 return e;
792 }
793
794 public String setValue(final String value) {
795 throw new RuntimeException("should not be called, don't use UnsupportedOp here");
796 }
797 }
798 }