1 /* 2 * Copyright (C) 2007 The Guava Authors 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 17 package com.google.common.collect; 18 19 import static com.google.common.collect.testing.Helpers.nefariousMapEntry; 20 import static org.junit.contrib.truth.Truth.ASSERT; 21 22 import com.google.common.annotations.GwtCompatible; 23 import com.google.common.annotations.GwtIncompatible; 24 import com.google.common.base.Supplier; 25 import com.google.common.testing.SerializableTester; 26 27 import junit.framework.TestCase; 28 29 import java.io.Serializable; 30 import java.util.AbstractMap; 31 import java.util.Arrays; 32 import java.util.Collection; 33 import java.util.Collections; 34 import java.util.Comparator; 35 import java.util.HashMap; 36 import java.util.Iterator; 37 import java.util.LinkedList; 38 import java.util.List; 39 import java.util.Map; 40 import java.util.Queue; 41 import java.util.RandomAccess; 42 import java.util.Set; 43 import java.util.SortedSet; 44 45 /** 46 * Tests for {@code MapConstraints}. 47 * 48 * @author Mike Bostock 49 * @author Jared Levy 50 */ 51 @GwtCompatible(emulated = true) 52 public class MapConstraintsTest extends TestCase { 53 54 private static final String TEST_KEY = "test"; 55 56 private static final Integer TEST_VALUE = 42; 57 58 static final class TestKeyException extends IllegalArgumentException { 59 private static final long serialVersionUID = 0; 60 } 61 62 static final class TestValueException extends IllegalArgumentException { 63 private static final long serialVersionUID = 0; 64 } 65 66 static final MapConstraint<String, Integer> TEST_CONSTRAINT 67 = new TestConstraint(); 68 69 private static final class TestConstraint 70 implements MapConstraint<String, Integer>, Serializable { 71 @Override 72 public void checkKeyValue(String key, Integer value) { 73 if (TEST_KEY.equals(key)) { 74 throw new TestKeyException(); 75 } 76 if (TEST_VALUE.equals(value)) { 77 throw new TestValueException(); 78 } 79 } 80 private static final long serialVersionUID = 0; 81 } 82 83 public void testNotNull() { 84 MapConstraint<Object, Object> constraint = MapConstraints.notNull(); 85 constraint.checkKeyValue("foo", 1); 86 assertEquals("Not null", constraint.toString()); 87 try { 88 constraint.checkKeyValue(null, 1); 89 fail("NullPointerException expected"); 90 } catch (NullPointerException expected) {} 91 try { 92 constraint.checkKeyValue("foo", null); 93 fail("NullPointerException expected"); 94 } catch (NullPointerException expected) {} 95 try { 96 constraint.checkKeyValue(null, null); 97 fail("NullPointerException expected"); 98 } catch (NullPointerException expected) {} 99 } 100 101 public void testConstrainedMapLegal() { 102 Map<String, Integer> map = Maps.newLinkedHashMap(); 103 Map<String, Integer> constrained = MapConstraints.constrainedMap( 104 map, TEST_CONSTRAINT); 105 map.put(TEST_KEY, TEST_VALUE); 106 constrained.put("foo", 1); 107 map.putAll(ImmutableMap.of("bar", 2)); 108 constrained.putAll(ImmutableMap.of("baz", 3)); 109 assertTrue(map.equals(constrained)); 110 assertTrue(constrained.equals(map)); 111 assertEquals(map.entrySet(), constrained.entrySet()); 112 assertEquals(map.keySet(), constrained.keySet()); 113 assertEquals(HashMultiset.create(map.values()), 114 HashMultiset.create(constrained.values())); 115 assertFalse(map.values() instanceof Serializable); 116 assertEquals(map.toString(), constrained.toString()); 117 assertEquals(map.hashCode(), constrained.hashCode()); 118 ASSERT.that(map.entrySet()).hasContentsInOrder( 119 Maps.immutableEntry(TEST_KEY, TEST_VALUE), 120 Maps.immutableEntry("foo", 1), 121 Maps.immutableEntry("bar", 2), 122 Maps.immutableEntry("baz", 3)); 123 } 124 125 public void testConstrainedMapIllegal() { 126 Map<String, Integer> map = Maps.newLinkedHashMap(); 127 Map<String, Integer> constrained = MapConstraints.constrainedMap( 128 map, TEST_CONSTRAINT); 129 try { 130 constrained.put(TEST_KEY, TEST_VALUE); 131 fail("TestKeyException expected"); 132 } catch (TestKeyException expected) {} 133 try { 134 constrained.put("baz", TEST_VALUE); 135 fail("TestValueException expected"); 136 } catch (TestValueException expected) {} 137 try { 138 constrained.put(TEST_KEY, 3); 139 fail("TestKeyException expected"); 140 } catch (TestKeyException expected) {} 141 try { 142 constrained.putAll(ImmutableMap.of("baz", 3, TEST_KEY, 4)); 143 fail("TestKeyException expected"); 144 } catch (TestKeyException expected) {} 145 assertEquals(Collections.emptySet(), map.entrySet()); 146 assertEquals(Collections.emptySet(), constrained.entrySet()); 147 } 148 149 public void testConstrainedBiMapLegal() { 150 BiMap<String, Integer> map = new AbstractBiMap<String, Integer>( 151 Maps.<String, Integer>newLinkedHashMap(), 152 Maps.<Integer, String>newLinkedHashMap()) {}; 153 BiMap<String, Integer> constrained = MapConstraints.constrainedBiMap( 154 map, TEST_CONSTRAINT); 155 map.put(TEST_KEY, TEST_VALUE); 156 constrained.put("foo", 1); 157 map.putAll(ImmutableMap.of("bar", 2)); 158 constrained.putAll(ImmutableMap.of("baz", 3)); 159 assertTrue(map.equals(constrained)); 160 assertTrue(constrained.equals(map)); 161 assertEquals(map.entrySet(), constrained.entrySet()); 162 assertEquals(map.keySet(), constrained.keySet()); 163 assertEquals(map.values(), constrained.values()); 164 assertEquals(map.toString(), constrained.toString()); 165 assertEquals(map.hashCode(), constrained.hashCode()); 166 ASSERT.that(map.entrySet()).hasContentsInOrder( 167 Maps.immutableEntry(TEST_KEY, TEST_VALUE), 168 Maps.immutableEntry("foo", 1), 169 Maps.immutableEntry("bar", 2), 170 Maps.immutableEntry("baz", 3)); 171 } 172 173 public void testConstrainedBiMapIllegal() { 174 BiMap<String, Integer> map = new AbstractBiMap<String, Integer>( 175 Maps.<String, Integer>newLinkedHashMap(), 176 Maps.<Integer, String>newLinkedHashMap()) {}; 177 BiMap<String, Integer> constrained = MapConstraints.constrainedBiMap( 178 map, TEST_CONSTRAINT); 179 try { 180 constrained.put(TEST_KEY, TEST_VALUE); 181 fail("TestKeyException expected"); 182 } catch (TestKeyException expected) {} 183 try { 184 constrained.put("baz", TEST_VALUE); 185 fail("TestValueException expected"); 186 } catch (TestValueException expected) {} 187 try { 188 constrained.put(TEST_KEY, 3); 189 fail("TestKeyException expected"); 190 } catch (TestKeyException expected) {} 191 try { 192 constrained.putAll(ImmutableMap.of("baz", 3, TEST_KEY, 4)); 193 fail("TestKeyException expected"); 194 } catch (TestKeyException expected) {} 195 try { 196 constrained.forcePut(TEST_KEY, 3); 197 fail("TestKeyException expected"); 198 } catch (TestKeyException expected) {} 199 try { 200 constrained.inverse().forcePut(TEST_VALUE, "baz"); 201 fail("TestValueException expected"); 202 } catch (TestValueException expected) {} 203 try { 204 constrained.inverse().forcePut(3, TEST_KEY); 205 fail("TestKeyException expected"); 206 } catch (TestKeyException expected) {} 207 assertEquals(Collections.emptySet(), map.entrySet()); 208 assertEquals(Collections.emptySet(), constrained.entrySet()); 209 } 210 211 public void testConstrainedMultimapLegal() { 212 Multimap<String, Integer> multimap = LinkedListMultimap.create(); 213 Multimap<String, Integer> constrained = MapConstraints.constrainedMultimap( 214 multimap, TEST_CONSTRAINT); 215 multimap.put(TEST_KEY, TEST_VALUE); 216 constrained.put("foo", 1); 217 multimap.get("bar").add(2); 218 constrained.get("baz").add(3); 219 multimap.get("qux").addAll(Arrays.asList(4)); 220 constrained.get("zig").addAll(Arrays.asList(5)); 221 multimap.putAll("zag", Arrays.asList(6)); 222 constrained.putAll("bee", Arrays.asList(7)); 223 multimap.putAll(new ImmutableMultimap.Builder<String, Integer>() 224 .put("bim", 8).build()); 225 constrained.putAll(new ImmutableMultimap.Builder<String, Integer>() 226 .put("bop", 9).build()); 227 multimap.putAll(new ImmutableMultimap.Builder<String, Integer>() 228 .put("dig", 10).build()); 229 constrained.putAll(new ImmutableMultimap.Builder<String, Integer>() 230 .put("dag", 11).build()); 231 assertTrue(multimap.equals(constrained)); 232 assertTrue(constrained.equals(multimap)); 233 ASSERT.that(ImmutableList.copyOf(multimap.entries())) 234 .is(ImmutableList.copyOf(constrained.entries())); 235 ASSERT.that(constrained.asMap().get("foo")).hasContentsInOrder(1); 236 assertNull(constrained.asMap().get("missing")); 237 assertEquals(multimap.asMap(), constrained.asMap()); 238 assertEquals(multimap.values(), constrained.values()); 239 assertEquals(multimap.keys(), constrained.keys()); 240 assertEquals(multimap.keySet(), constrained.keySet()); 241 assertEquals(multimap.toString(), constrained.toString()); 242 assertEquals(multimap.hashCode(), constrained.hashCode()); 243 ASSERT.that(multimap.entries()).hasContentsInOrder( 244 Maps.immutableEntry(TEST_KEY, TEST_VALUE), 245 Maps.immutableEntry("foo", 1), 246 Maps.immutableEntry("bar", 2), 247 Maps.immutableEntry("baz", 3), 248 Maps.immutableEntry("qux", 4), 249 Maps.immutableEntry("zig", 5), 250 Maps.immutableEntry("zag", 6), 251 Maps.immutableEntry("bee", 7), 252 Maps.immutableEntry("bim", 8), 253 Maps.immutableEntry("bop", 9), 254 Maps.immutableEntry("dig", 10), 255 Maps.immutableEntry("dag", 11)); 256 assertFalse(constrained.asMap().values() instanceof Serializable); 257 Iterator<Collection<Integer>> iterator = 258 constrained.asMap().values().iterator(); 259 iterator.next(); 260 iterator.next().add(12); 261 assertTrue(multimap.containsEntry("foo", 12)); 262 } 263 264 public void testConstrainedTypePreservingList() { 265 ListMultimap<String, Integer> multimap 266 = MapConstraints.constrainedListMultimap( 267 LinkedListMultimap.<String, Integer>create(), 268 TEST_CONSTRAINT); 269 multimap.put("foo", 1); 270 Map.Entry<String, Collection<Integer>> entry 271 = multimap.asMap().entrySet().iterator().next(); 272 assertTrue(entry.getValue() instanceof List); 273 assertFalse(multimap.entries() instanceof Set); 274 assertFalse(multimap.get("foo") instanceof RandomAccess); 275 } 276 277 public void testConstrainedTypePreservingRandomAccessList() { 278 ListMultimap<String, Integer> multimap 279 = MapConstraints.constrainedListMultimap( 280 ArrayListMultimap.<String, Integer>create(), 281 TEST_CONSTRAINT); 282 multimap.put("foo", 1); 283 Map.Entry<String, Collection<Integer>> entry 284 = multimap.asMap().entrySet().iterator().next(); 285 assertTrue(entry.getValue() instanceof List); 286 assertFalse(multimap.entries() instanceof Set); 287 assertTrue(multimap.get("foo") instanceof RandomAccess); 288 } 289 290 public void testConstrainedTypePreservingSet() { 291 SetMultimap<String, Integer> multimap 292 = MapConstraints.constrainedSetMultimap( 293 LinkedHashMultimap.<String, Integer>create(), 294 TEST_CONSTRAINT); 295 multimap.put("foo", 1); 296 Map.Entry<String, Collection<Integer>> entry 297 = multimap.asMap().entrySet().iterator().next(); 298 assertTrue(entry.getValue() instanceof Set); 299 } 300 301 public void testConstrainedTypePreservingSortedSet() { 302 Comparator<Integer> comparator = Collections.reverseOrder(); 303 SortedSetMultimap<String, Integer> delegate 304 = TreeMultimap.create(Ordering.<String>natural(), comparator); 305 SortedSetMultimap<String, Integer> multimap 306 = MapConstraints.constrainedSortedSetMultimap(delegate, 307 TEST_CONSTRAINT); 308 multimap.put("foo", 1); 309 Map.Entry<String, Collection<Integer>> entry 310 = multimap.asMap().entrySet().iterator().next(); 311 assertTrue(entry.getValue() instanceof SortedSet); 312 assertSame(comparator, multimap.valueComparator()); 313 assertSame(comparator, multimap.get("foo").comparator()); 314 } 315 316 @SuppressWarnings("unchecked") 317 public void testConstrainedMultimapIllegal() { 318 Multimap<String, Integer> multimap = LinkedListMultimap.create(); 319 Multimap<String, Integer> constrained = MapConstraints.constrainedMultimap( 320 multimap, TEST_CONSTRAINT); 321 try { 322 constrained.put(TEST_KEY, 1); 323 fail("TestKeyException expected"); 324 } catch (TestKeyException expected) {} 325 try { 326 constrained.put("foo", TEST_VALUE); 327 fail("TestValueException expected"); 328 } catch (TestValueException expected) {} 329 try { 330 constrained.put(TEST_KEY, TEST_VALUE); 331 fail("TestKeyException expected"); 332 } catch (TestKeyException expected) {} 333 try { 334 constrained.get(TEST_KEY).add(1); 335 fail("TestKeyException expected"); 336 } catch (TestKeyException expected) {} 337 try { 338 constrained.get("foo").add(TEST_VALUE); 339 fail("TestValueException expected"); 340 } catch (TestValueException expected) {} 341 try { 342 constrained.get(TEST_KEY).add(TEST_VALUE); 343 fail("TestKeyException expected"); 344 } catch (TestKeyException expected) {} 345 try { 346 constrained.get(TEST_KEY).addAll(Arrays.asList(1)); 347 fail("TestKeyException expected"); 348 } catch (TestKeyException expected) {} 349 try { 350 constrained.get("foo").addAll(Arrays.asList(1, TEST_VALUE)); 351 fail("TestValueException expected"); 352 } catch (TestValueException expected) {} 353 try { 354 constrained.get(TEST_KEY).addAll(Arrays.asList(1, TEST_VALUE)); 355 fail("TestKeyException expected"); 356 } catch (TestKeyException expected) {} 357 try { 358 constrained.putAll(TEST_KEY, Arrays.asList(1)); 359 fail("TestKeyException expected"); 360 } catch (TestKeyException expected) {} 361 try { 362 constrained.putAll("foo", Arrays.asList(1, TEST_VALUE)); 363 fail("TestValueException expected"); 364 } catch (TestValueException expected) {} 365 try { 366 constrained.putAll(TEST_KEY, Arrays.asList(1, TEST_VALUE)); 367 fail("TestKeyException expected"); 368 } catch (TestKeyException expected) {} 369 try { 370 constrained.putAll(new ImmutableMultimap.Builder<String, Integer>() 371 .put(TEST_KEY, 2).put("foo", 1).build()); 372 fail("TestKeyException expected"); 373 } catch (TestKeyException expected) {} 374 try { 375 constrained.putAll(new ImmutableMultimap.Builder<String, Integer>() 376 .put("bar", TEST_VALUE).put("foo", 1).build()); 377 fail("TestValueException expected"); 378 } catch (TestValueException expected) {} 379 try { 380 constrained.putAll(new ImmutableMultimap.Builder<String, Integer>() 381 .put(TEST_KEY, TEST_VALUE).put("foo", 1).build()); 382 fail("TestKeyException expected"); 383 } catch (TestKeyException expected) {} 384 try { 385 constrained.entries().add(Maps.immutableEntry(TEST_KEY, 1)); 386 fail("UnsupportedOperationException expected"); 387 } catch (UnsupportedOperationException expected) {} 388 try { 389 constrained.entries().addAll(Arrays.asList( 390 Maps.immutableEntry("foo", 1), 391 Maps.immutableEntry(TEST_KEY, 2))); 392 fail("UnsupportedOperationException expected"); 393 } catch (UnsupportedOperationException expected) {} 394 assertTrue(multimap.isEmpty()); 395 assertTrue(constrained.isEmpty()); 396 constrained.put("foo", 1); 397 try { 398 constrained.asMap().get("foo").add(TEST_VALUE); 399 fail("TestValueException expected"); 400 } catch (TestValueException expected) {} 401 try { 402 constrained.asMap().values().iterator().next().add(TEST_VALUE); 403 fail("TestValueException expected"); 404 } catch (TestValueException expected) {} 405 try { 406 ((Collection<Integer>) constrained.asMap().values().toArray()[0]) 407 .add(TEST_VALUE); 408 fail("TestValueException expected"); 409 } catch (TestValueException expected) {} 410 ASSERT.that(ImmutableList.copyOf(multimap.entries())) 411 .is(ImmutableList.copyOf(constrained.entries())); 412 assertEquals(multimap.asMap(), constrained.asMap()); 413 assertEquals(multimap.values(), constrained.values()); 414 assertEquals(multimap.keys(), constrained.keys()); 415 assertEquals(multimap.keySet(), constrained.keySet()); 416 assertEquals(multimap.toString(), constrained.toString()); 417 assertEquals(multimap.hashCode(), constrained.hashCode()); 418 } 419 420 private static class QueueSupplier implements Supplier<Queue<Integer>> { 421 @Override 422 public Queue<Integer> get() { 423 return new LinkedList<Integer>(); 424 } 425 } 426 427 public void testConstrainedMultimapQueue() { 428 Multimap<String, Integer> multimap = Multimaps.newMultimap( 429 new HashMap<String, Collection<Integer>>(), new QueueSupplier()); 430 Multimap<String, Integer> constrained = MapConstraints.constrainedMultimap( 431 multimap, TEST_CONSTRAINT); 432 constrained.put("foo", 1); 433 assertTrue(constrained.get("foo").contains(1)); 434 assertTrue(multimap.get("foo").contains(1)); 435 try { 436 constrained.put(TEST_KEY, 1); 437 fail("TestKeyException expected"); 438 } catch (TestKeyException expected) {} 439 try { 440 constrained.put("foo", TEST_VALUE); 441 fail("TestValueException expected"); 442 } catch (TestValueException expected) {} 443 try { 444 constrained.get("foo").add(TEST_VALUE); 445 fail("TestKeyException expected"); 446 } catch (TestValueException expected) {} 447 try { 448 constrained.get(TEST_KEY).add(1); 449 fail("TestValueException expected"); 450 } catch (TestKeyException expected) {} 451 assertEquals(1, constrained.size()); 452 assertEquals(1, multimap.size()); 453 } 454 455 public void testMapEntrySetToArray() { 456 Map<String, Integer> map = Maps.newLinkedHashMap(); 457 Map<String, Integer> constrained 458 = MapConstraints.constrainedMap(map, TEST_CONSTRAINT); 459 map.put("foo", 1); 460 @SuppressWarnings("unchecked") 461 Map.Entry<String, Integer> entry 462 = (Map.Entry) constrained.entrySet().toArray()[0]; 463 try { 464 entry.setValue(TEST_VALUE); 465 fail("TestValueException expected"); 466 } catch (TestValueException expected) {} 467 assertFalse(map.containsValue(TEST_VALUE)); 468 } 469 470 public void testMapEntrySetContainsNefariousEntry() { 471 Map<String, Integer> map = Maps.newTreeMap(); 472 Map<String, Integer> constrained 473 = MapConstraints.constrainedMap(map, TEST_CONSTRAINT); 474 map.put("foo", 1); 475 Map.Entry<String, Integer> nefariousEntry 476 = nefariousMapEntry(TEST_KEY, TEST_VALUE); 477 Set<Map.Entry<String, Integer>> entries = constrained.entrySet(); 478 assertFalse(entries.contains(nefariousEntry)); 479 assertFalse(map.containsValue(TEST_VALUE)); 480 assertFalse(entries.containsAll(Collections.singleton(nefariousEntry))); 481 assertFalse(map.containsValue(TEST_VALUE)); 482 } 483 484 public void testMultimapAsMapEntriesToArray() { 485 Multimap<String, Integer> multimap = LinkedListMultimap.create(); 486 Multimap<String, Integer> constrained 487 = MapConstraints.constrainedMultimap(multimap, TEST_CONSTRAINT); 488 multimap.put("foo", 1); 489 @SuppressWarnings("unchecked") 490 Map.Entry<String, Collection<Integer>> entry 491 = (Map.Entry<String, Collection<Integer>>) 492 constrained.asMap().entrySet().toArray()[0]; 493 try { 494 entry.setValue(Collections.<Integer>emptySet()); 495 fail("UnsupportedOperationException expected"); 496 } catch (UnsupportedOperationException expected) {} 497 try { 498 entry.getValue().add(TEST_VALUE); 499 fail("TestValueException expected"); 500 } catch (TestValueException expected) {} 501 assertFalse(multimap.containsValue(TEST_VALUE)); 502 } 503 504 public void testMultimapAsMapValuesToArray() { 505 Multimap<String, Integer> multimap = LinkedListMultimap.create(); 506 Multimap<String, Integer> constrained 507 = MapConstraints.constrainedMultimap(multimap, TEST_CONSTRAINT); 508 multimap.put("foo", 1); 509 @SuppressWarnings("unchecked") 510 Collection<Integer> collection 511 = (Collection<Integer>) constrained.asMap().values().toArray()[0]; 512 try { 513 collection.add(TEST_VALUE); 514 fail("TestValueException expected"); 515 } catch (TestValueException expected) {} 516 assertFalse(multimap.containsValue(TEST_VALUE)); 517 } 518 519 public void testMultimapEntriesContainsNefariousEntry() { 520 Multimap<String, Integer> multimap = LinkedListMultimap.create(); 521 Multimap<String, Integer> constrained 522 = MapConstraints.constrainedMultimap(multimap, TEST_CONSTRAINT); 523 multimap.put("foo", 1); 524 Map.Entry<String, Integer> nefariousEntry 525 = nefariousMapEntry(TEST_KEY, TEST_VALUE); 526 Collection<Map.Entry<String, Integer>> entries = constrained.entries(); 527 assertFalse(entries.contains(nefariousEntry)); 528 assertFalse(multimap.containsValue(TEST_VALUE)); 529 assertFalse(entries.containsAll(Collections.singleton(nefariousEntry))); 530 assertFalse(multimap.containsValue(TEST_VALUE)); 531 } 532 533 public void testMultimapEntriesRemoveNefariousEntry() { 534 Multimap<String, Integer> multimap = LinkedListMultimap.create(); 535 Multimap<String, Integer> constrained 536 = MapConstraints.constrainedMultimap(multimap, TEST_CONSTRAINT); 537 multimap.put("foo", 1); 538 Map.Entry<String, Integer> nefariousEntry 539 = nefariousMapEntry(TEST_KEY, TEST_VALUE); 540 Collection<Map.Entry<String, Integer>> entries = constrained.entries(); 541 assertFalse(entries.remove(nefariousEntry)); 542 assertFalse(multimap.containsValue(TEST_VALUE)); 543 assertFalse(entries.removeAll(Collections.singleton(nefariousEntry))); 544 assertFalse(multimap.containsValue(TEST_VALUE)); 545 } 546 547 public void testMultimapAsMapEntriesContainsNefariousEntry() { 548 Multimap<String, Integer> multimap = LinkedListMultimap.create(); 549 Multimap<String, Integer> constrained 550 = MapConstraints.constrainedMultimap(multimap, TEST_CONSTRAINT); 551 multimap.put("foo", 1); 552 Map.Entry<String, ? extends Collection<Integer>> nefariousEntry 553 = nefariousMapEntry(TEST_KEY, Collections.singleton(TEST_VALUE)); 554 Set<Map.Entry<String, Collection<Integer>>> entries 555 = constrained.asMap().entrySet(); 556 assertFalse(entries.contains(nefariousEntry)); 557 assertFalse(multimap.containsValue(TEST_VALUE)); 558 assertFalse(entries.containsAll(Collections.singleton(nefariousEntry))); 559 assertFalse(multimap.containsValue(TEST_VALUE)); 560 } 561 562 public void testMultimapAsMapEntriesRemoveNefariousEntry() { 563 Multimap<String, Integer> multimap = LinkedListMultimap.create(); 564 Multimap<String, Integer> constrained 565 = MapConstraints.constrainedMultimap(multimap, TEST_CONSTRAINT); 566 multimap.put("foo", 1); 567 Map.Entry<String, ? extends Collection<Integer>> nefariousEntry 568 = nefariousMapEntry(TEST_KEY, Collections.singleton(TEST_VALUE)); 569 Set<Map.Entry<String, Collection<Integer>>> entries 570 = constrained.asMap().entrySet(); 571 assertFalse(entries.remove(nefariousEntry)); 572 assertFalse(multimap.containsValue(TEST_VALUE)); 573 assertFalse(entries.removeAll(Collections.singleton(nefariousEntry))); 574 assertFalse(multimap.containsValue(TEST_VALUE)); 575 } 576 577 public void testNefariousMapPutAll() { 578 Map<String, Integer> map = Maps.newLinkedHashMap(); 579 Map<String, Integer> constrained = MapConstraints.constrainedMap( 580 map, TEST_CONSTRAINT); 581 Map<String, Integer> onceIterable = onceIterableMap("foo", 1); 582 constrained.putAll(onceIterable); 583 assertEquals((Integer) 1, constrained.get("foo")); 584 } 585 586 public void testNefariousMultimapPutAllIterable() { 587 Multimap<String, Integer> multimap = LinkedListMultimap.create(); 588 Multimap<String, Integer> constrained = MapConstraints.constrainedMultimap( 589 multimap, TEST_CONSTRAINT); 590 Collection<Integer> onceIterable 591 = ConstraintsTest.onceIterableCollection(1); 592 constrained.putAll("foo", onceIterable); 593 assertEquals(ImmutableList.of(1), constrained.get("foo")); 594 } 595 596 public void testNefariousMultimapPutAllMultimap() { 597 Multimap<String, Integer> multimap = LinkedListMultimap.create(); 598 Multimap<String, Integer> constrained = MapConstraints.constrainedMultimap( 599 multimap, TEST_CONSTRAINT); 600 Multimap<String, Integer> onceIterable 601 = Multimaps.forMap(onceIterableMap("foo", 1)); 602 constrained.putAll(onceIterable); 603 assertEquals(ImmutableList.of(1), constrained.get("foo")); 604 } 605 606 public void testNefariousMultimapGetAddAll() { 607 Multimap<String, Integer> multimap = LinkedListMultimap.create(); 608 Multimap<String, Integer> constrained = MapConstraints.constrainedMultimap( 609 multimap, TEST_CONSTRAINT); 610 Collection<Integer> onceIterable 611 = ConstraintsTest.onceIterableCollection(1); 612 constrained.get("foo").addAll(onceIterable); 613 assertEquals(ImmutableList.of(1), constrained.get("foo")); 614 } 615 616 /** 617 * Returns a "nefarious" map, which permits only one call to its views' 618 * iterator() methods. This verifies that the constrained map uses a 619 * defensive copy instead of potentially checking the elements in one snapshot 620 * and adding the elements from another. 621 * 622 * @param key the key to be contained in the map 623 * @param value the value to be contained in the map 624 */ 625 static <K, V> Map<K, V> onceIterableMap(K key, V value) { 626 final Map.Entry<K, V> entry = Maps.immutableEntry(key, value); 627 return new AbstractMap<K, V>() { 628 boolean iteratorCalled; 629 @Override public int size() { 630 /* 631 * We could make the map empty, but that seems more likely to trigger 632 * special cases (so maybe we should test both empty and nonempty...). 633 */ 634 return 1; 635 } 636 @Override public Set<Entry<K, V>> entrySet() { 637 return new ForwardingSet<Entry<K, V>>() { 638 @Override protected Set<Entry<K, V>> delegate() { 639 return Collections.singleton(entry); 640 } 641 @Override public Iterator<Entry<K, V>> iterator() { 642 assertFalse("Expected only one call to iterator()", iteratorCalled); 643 iteratorCalled = true; 644 return super.iterator(); 645 } 646 }; 647 } 648 @Override public Set<K> keySet() { 649 throw new UnsupportedOperationException(); 650 } 651 @Override public Collection<V> values() { 652 throw new UnsupportedOperationException(); 653 } 654 }; 655 } 656 657 @GwtIncompatible("SerializableTester") 658 public void testSerialization() { 659 // TODO: Test serialization of constrained collections. 660 assertSame(MapConstraints.notNull(), 661 SerializableTester.reserialize(MapConstraints.notNull())); 662 } 663 } 664