1 /* 2 * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javafx.collections; 27 28 import org.junit.Before; 29 import org.junit.Test; 30 31 import java.util.Arrays; 32 import java.util.Collection; 33 import java.util.HashMap; 34 import java.util.Iterator; 35 import java.util.Map; 36 37 import static javafx.collections.MockMapObserver.Call.call; 38 import static javafx.collections.MockMapObserver.Tuple.tup; 39 import static org.junit.Assert.*; 40 import static org.junit.Assert.assertEquals; 41 42 import org.junit.runner.RunWith; 43 import org.junit.runners.Parameterized; 44 45 @RunWith(Parameterized.class) 46 public class ObservableMapTest { 47 48 final Callable<ObservableMap<String, String>> mapFactory; 49 private ObservableMap<String, String> observableMap; 50 private MockMapObserver<String, String> observer; 51 52 53 public ObservableMapTest(final Callable<ObservableMap<String, String>> mapFactory) { 54 this.mapFactory = mapFactory; 55 } 56 57 @Parameterized.Parameters 58 public static Collection createParameters() { 59 Object[][] data = new Object[][] { 60 { TestedObservableMaps.HASH_MAP }, 61 { TestedObservableMaps.TREE_MAP }, 62 { TestedObservableMaps.LINKED_HASH_MAP }, 63 { TestedObservableMaps.CONCURRENT_HASH_MAP }, 64 { TestedObservableMaps.CHECKED_OBSERVABLE_HASH_MAP }, 65 { TestedObservableMaps.SYNCHRONIZED_OBSERVABLE_HASH_MAP }, 66 { TestedObservableMaps.OBSERVABLE_MAP_PROPERTY } 67 }; 68 return Arrays.asList(data); 69 } 70 71 @Before 72 public void setUp() throws Exception { 73 observableMap = mapFactory.call(); 74 observer = new MockMapObserver<String, String>(); 75 observableMap.addListener(observer); 76 77 useMapData(); 78 } 79 80 /** 81 * Modifies the map in the fixture to use the strings passed in instead of 82 * the default strings, and re-creates the observable map and the observer. 83 * If no strings are passed in, the result is an empty map. 84 * 85 * @param strings the strings to use for the list in the fixture 86 */ 87 void useMapData(String... strings) { 88 observableMap.clear(); 89 observableMap.put("one", "1"); 90 observableMap.put("two", "2"); 91 observableMap.put("foo", "bar"); 92 observer.clear(); 93 } 94 95 @Test 96 public void testPutRemove() { 97 observableMap.put("observedFoo", "barVal"); 98 observableMap.put("foo", "barfoo"); 99 assertEquals("barVal", observableMap.get("observedFoo")); 100 101 observableMap.remove("observedFoo"); 102 observableMap.remove("foo"); 103 observableMap.remove("bar"); 104 observableMap.put("one", "1"); 105 106 assertFalse(observableMap.containsKey("foo")); 107 108 observer.assertAdded(0, tup("observedFoo", "barVal")); 109 observer.assertAdded(1, tup("foo", "barfoo")); 110 observer.assertRemoved(1, tup("foo", "bar")); 111 observer.assertRemoved(2, tup("observedFoo", "barVal")); 112 observer.assertRemoved(3, tup("foo", "barfoo")); 113 114 assertEquals(observer.getCallsNumber(), 4); 115 } 116 117 @Test 118 public void testPutRemove_Null() { 119 if (mapFactory instanceof TestedObservableMaps.CallableConcurrentHashMapImpl) { 120 return; // Do not perform on ConcurrentHashMap, as it doesn't accept nulls 121 } 122 observableMap.clear(); 123 observer.clear(); 124 125 observableMap.put("bar", null); 126 observableMap.put("foo", "x"); 127 observableMap.put("bar", "x"); 128 observableMap.put("foo", null); 129 130 assertEquals(2, observableMap.size()); 131 132 observableMap.remove("bar"); 133 observableMap.remove("foo"); 134 135 assertEquals(0, observableMap.size()); 136 137 observer.assertAdded(0, tup("bar", (String)null)); 138 observer.assertAdded(1, tup("foo", "x")); 139 observer.assertAdded(2, tup("bar", "x")); 140 observer.assertRemoved(2, tup("bar", (String)null)); 141 observer.assertAdded(3, tup("foo", (String)null)); 142 observer.assertRemoved(3, tup("foo", "x")); 143 observer.assertRemoved(4, tup("bar", "x")); 144 observer.assertRemoved(5, tup("foo", (String)null)); 145 146 assertEquals(observer.getCallsNumber(), 6); 147 } 148 149 @Test 150 public void testPutRemove_NullKey() { 151 if (mapFactory instanceof TestedObservableMaps.CallableConcurrentHashMapImpl || 152 mapFactory instanceof TestedObservableMaps.CallableTreeMapImpl) { 153 return; // Do not perform on ConcurrentHashMap and TreeMap, as they doesn't accept null keys 154 } 155 156 observableMap.put(null, "abc"); 157 158 assertEquals(4, observableMap.size()); 159 160 observableMap.remove(null); 161 162 assertEquals(3, observableMap.size()); 163 164 observer.assertAdded(0, tup((String)null, "abc")); 165 observer.assertRemoved(1, tup((String)null, "abc")); 166 167 assertEquals(observer.getCallsNumber(), 2); 168 } 169 170 @Test 171 @SuppressWarnings("unchecked") 172 public void testPutAll() { 173 Map<String, String> map = new HashMap<String, String>(); 174 map.put("oFoo", "OFoo"); 175 map.put("pFoo", "PFoo"); 176 map.put("foo", "foofoo"); 177 map.put("one", "1"); 178 observableMap.putAll(map); 179 180 assertTrue(observableMap.containsKey("oFoo")); 181 observer.assertMultipleCalls(call("oFoo", null, "OFoo"), call("pFoo", null, "PFoo"), call("foo", "bar", "foofoo")); 182 } 183 184 @Test 185 @SuppressWarnings("unchecked") 186 public void testClear() { 187 observableMap.clear(); 188 189 assertTrue(observableMap.isEmpty()); 190 observer.assertMultipleRemoved(tup("one", "1"), tup("two", "2"), tup("foo", "bar")); 191 192 } 193 194 @Test 195 public void testOther() { 196 assertEquals(3, observableMap.size()); 197 assertFalse(observableMap.isEmpty()); 198 199 assertTrue(observableMap.containsKey("foo")); 200 assertFalse(observableMap.containsKey("bar")); 201 202 assertFalse(observableMap.containsValue("foo")); 203 assertTrue(observableMap.containsValue("bar")); 204 } 205 206 @Test 207 public void testKeySet_Remove() { 208 observableMap.keySet().remove("one"); 209 observableMap.keySet().remove("two"); 210 observableMap.keySet().remove("three"); 211 212 observer.assertRemoved(0, tup("one", "1")); 213 observer.assertRemoved(1, tup("two", "2")); 214 assertTrue(observer.getCallsNumber() == 2); 215 } 216 217 @Test 218 @SuppressWarnings("unchecked") 219 public void testKeySet_RemoveAll() { 220 observableMap.keySet().removeAll(Arrays.asList("one", "two", "three")); 221 222 observer.assertMultipleRemoved(tup("one", "1"), tup("two", "2")); 223 assertTrue(observableMap.size() == 1); 224 } 225 226 @Test 227 public void testKeySet_RetainAll() { 228 observableMap.keySet().retainAll(Arrays.asList("one", "two", "three")); 229 230 observer.assertRemoved(tup("foo", "bar")); 231 assertTrue(observableMap.size() == 2); 232 } 233 234 @Test 235 @SuppressWarnings("unchecked") 236 public void testKeySet_Clear() { 237 observableMap.keySet().clear(); 238 assertTrue(observableMap.keySet().isEmpty()); 239 observer.assertMultipleRemoved(tup("one", "1"), tup("two", "2"), tup("foo", "bar")); 240 } 241 242 @Test 243 public void testKeySet_Iterator() { 244 Iterator<String> iterator = observableMap.keySet().iterator(); 245 assertTrue(iterator.hasNext()); 246 247 String toBeRemoved = iterator.next(); 248 String toBeRemovedVal = observableMap.get(toBeRemoved); 249 iterator.remove(); 250 251 assertTrue(observableMap.size() == 2); 252 observer.assertRemoved(tup(toBeRemoved, toBeRemovedVal)); 253 } 254 255 @Test 256 public void testKeySet_Other() { 257 assertEquals(3, observableMap.keySet().size()); 258 assertTrue(observableMap.keySet().contains("foo")); 259 assertFalse(observableMap.keySet().contains("bar")); 260 261 assertTrue(observableMap.keySet().containsAll(Arrays.asList("one", "two"))); 262 assertFalse(observableMap.keySet().containsAll(Arrays.asList("one", "three"))); 263 264 assertTrue(observableMap.keySet().toArray(new String[0]).length == 3); 265 assertTrue(observableMap.keySet().toArray().length == 3); 266 } 267 268 @Test 269 public void testValues_Remove() { 270 observableMap.values().remove("1"); 271 observableMap.values().remove("2"); 272 observableMap.values().remove("3"); 273 274 observer.assertRemoved(0, tup("one", "1")); 275 observer.assertRemoved(1, tup("two", "2")); 276 assertTrue(observer.getCallsNumber() == 2); 277 } 278 279 @Test 280 @SuppressWarnings("unchecked") 281 public void testValues_RemoveAll() { 282 observableMap.values().removeAll(Arrays.asList("1", "2", "3")); 283 284 observer.assertMultipleRemoved(tup("one", "1"), tup("two", "2")); 285 assertTrue(observableMap.size() == 1); 286 } 287 288 @Test 289 public void testValues_RetainAll() { 290 observableMap.values().retainAll(Arrays.asList("1", "2", "3")); 291 292 observer.assertRemoved(tup("foo", "bar")); 293 assertTrue(observableMap.size() == 2); 294 } 295 296 @Test 297 @SuppressWarnings("unchecked") 298 public void testValues_Clear() { 299 observableMap.values().clear(); 300 assertTrue(observableMap.values().isEmpty()); 301 observer.assertMultipleRemoved(tup("one", "1"), tup("two", "2"), tup("foo", "bar")); 302 } 303 304 @Test 305 public void testValues_Iterator() { 306 Iterator<String> iterator = observableMap.values().iterator(); 307 assertTrue(iterator.hasNext()); 308 309 String toBeRemovedVal = iterator.next(); 310 iterator.remove(); 311 312 assertTrue(observableMap.size() == 2); 313 observer.assertRemoved(tup(toBeRemovedVal.equals("1") ? "one" 314 : toBeRemovedVal.equals("2") ? "two" 315 : toBeRemovedVal.equals("bar") ? "foo" : null, toBeRemovedVal)); 316 } 317 318 @Test 319 public void testValues_Other() { 320 assertEquals(3, observableMap.values().size()); 321 assertFalse(observableMap.values().contains("foo")); 322 assertTrue(observableMap.values().contains("bar")); 323 324 assertTrue(observableMap.values().containsAll(Arrays.asList("1", "2"))); 325 assertFalse(observableMap.values().containsAll(Arrays.asList("1", "3"))); 326 327 assertTrue(observableMap.values().toArray(new String[0]).length == 3); 328 assertTrue(observableMap.values().toArray().length == 3); 329 } 330 331 @Test 332 public void testEntrySet_Remove() { 333 observableMap.entrySet().remove(entry("one","1")); 334 observableMap.entrySet().remove(entry("two","2")); 335 observableMap.entrySet().remove(entry("three","3")); 336 337 observer.assertRemoved(0, tup("one", "1")); 338 observer.assertRemoved(1, tup("two", "2")); 339 assertTrue(observer.getCallsNumber() == 2); 340 } 341 342 @Test 343 @SuppressWarnings("unchecked") 344 public void testEntrySet_RemoveAll() { 345 observableMap.entrySet().removeAll(Arrays.asList(entry("one","1"), entry("two","2"), entry("three","3"))); 346 347 observer.assertMultipleRemoved(tup("one", "1"), tup("two", "2")); 348 assertTrue(observableMap.size() == 1); 349 } 350 351 @Test 352 @SuppressWarnings("unchecked") 353 public void testEntrySet_RetainAll() { 354 observableMap.entrySet().retainAll(Arrays.asList(entry("one","1"), entry("two","2"), entry("three","3"))); 355 356 observer.assertRemoved(tup("foo", "bar")); 357 assertTrue(observableMap.size() == 2); 358 } 359 360 @Test 361 @SuppressWarnings("unchecked") 362 public void testEntrySet_Clear() { 363 observableMap.entrySet().clear(); 364 assertTrue(observableMap.entrySet().isEmpty()); 365 observer.assertMultipleRemoved(tup("one", "1"), tup("two", "2"), tup("foo", "bar")); 366 } 367 368 @Test 369 public void testEntrySet_Iterator() { 370 Iterator<Map.Entry<String, String>> iterator = observableMap.entrySet().iterator(); 371 assertTrue(iterator.hasNext()); 372 373 Map.Entry<String, String> toBeRemoved = iterator.next(); 374 String toBeRemovedKey = toBeRemoved.getKey(); 375 String toBeRemovedVal = toBeRemoved.getValue(); 376 377 iterator.remove(); 378 379 assertTrue(observableMap.size() == 2); 380 observer.assertRemoved(tup(toBeRemovedKey, toBeRemovedVal)); 381 } 382 383 @Test 384 @SuppressWarnings("unchecked") 385 public void testEntrySet_Other() { 386 assertEquals(3, observableMap.entrySet().size()); 387 assertTrue(observableMap.entrySet().contains(entry("foo", "bar"))); 388 assertFalse(observableMap.entrySet().contains(entry("bar", "foo"))); 389 390 assertTrue(observableMap.entrySet().containsAll(Arrays.asList(entry("one","1"), entry("two","2")))); 391 assertFalse(observableMap.entrySet().containsAll(Arrays.asList(entry("one","1"), entry("three","3")))); 392 393 assertTrue(observableMap.entrySet().toArray(new Map.Entry[0]).length == 3); 394 assertTrue(observableMap.entrySet().toArray().length == 3); 395 } 396 397 @Test 398 public void testObserverCanRemoveObservers() { 399 final MapChangeListener<String, String> listObserver = change -> { 400 change.getMap().removeListener(observer); 401 }; 402 observableMap.addListener(listObserver); 403 observableMap.put("x", "x"); 404 observer.clear(); 405 observableMap.put("y", "y"); 406 observer.check0(); 407 observableMap.removeListener(listObserver); 408 409 final StringMapChangeListener listener = new StringMapChangeListener(); 410 observableMap.addListener(listener); 411 observableMap.put("z", "z"); 412 assertEquals(listener.counter, 1); 413 observableMap.put("zz", "zz"); 414 assertEquals(listener.counter, 1); 415 } 416 417 418 private static class StringMapChangeListener implements MapChangeListener<String, String> { 419 420 private int counter; 421 422 @Override 423 public void onChanged(Change<? extends String, ? extends String> change) { 424 change.getMap().removeListener(this); 425 ++counter; 426 } 427 } 428 429 @Test 430 public void testEqualsAndHashCode() { 431 final Map<String, String> other = new HashMap<>(observableMap); 432 assertTrue(observableMap.equals(other)); 433 assertEquals(observableMap.hashCode(), other.hashCode()); 434 } 435 436 private<K, V> Map.Entry<K, V> entry(final K key, final V value) { 437 return new Map.Entry<K, V>() { 438 439 @Override 440 public K getKey() { 441 return key; 442 } 443 444 @Override 445 public V getValue() { 446 return value; 447 } 448 449 @Override 450 public V setValue(V value) { 451 throw new UnsupportedOperationException("Not supported."); 452 } 453 454 @Override 455 public boolean equals(Object obj) { 456 if (!(obj instanceof Map.Entry)) { 457 return false; 458 } 459 Map.Entry entry = (Map.Entry)obj; 460 return (getKey()==null ? 461 entry.getKey()==null : getKey().equals(entry.getKey())) && 462 (getValue()==null ? 463 entry.getValue()==null : getValue().equals(entry.getValue())); 464 } 465 466 @Override 467 public int hashCode() { 468 return (getKey()==null ? 0 : getKey().hashCode()) ^ 469 (getValue()==null ? 0 : getValue().hashCode()); 470 } 471 472 }; 473 } 474 }