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