1 /* 2 * Copyright (c) 2012, 2013, 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.binding; 27 28 import com.sun.javafx.collections.annotations.ReturnsUnmodifiableCollection; 29 import test.javafx.beans.InvalidationListenerMock; 30 import javafx.beans.Observable; 31 import javafx.beans.binding.MapBinding; 32 import javafx.beans.property.ReadOnlyBooleanProperty; 33 import javafx.beans.property.ReadOnlyIntegerProperty; 34 import test.javafx.beans.value.ChangeListenerMock; 35 import javafx.beans.value.ObservableValueBase; 36 import javafx.collections.FXCollections; 37 import test.javafx.collections.MockMapObserver; 38 import javafx.collections.ObservableList; 39 import javafx.collections.ObservableMap; 40 import org.junit.Before; 41 import org.junit.Test; 42 43 import java.util.Collections; 44 import java.util.HashMap; 45 import java.util.Map; 46 47 import static test.javafx.collections.MockMapObserver.Call; 48 import static org.junit.Assert.*; 49 50 /** 51 */ 52 public class MapBindingTest { 53 54 private final static Object KEY_1 = new Object(); 55 private final static Object KEY_2_0 = new Object(); 56 private final static Object KEY_2_1 = new Object(); 57 private final static Object DATA_1 = new Object(); 58 private final static Object DATA_2_0 = new Object(); 59 private final static Object DATA_2_1 = new Object(); 60 61 private ObservableStub dependency1; 62 private ObservableStub dependency2; 63 64 private MapBindingImpl binding0; 65 private MapBindingImpl binding1; 66 private MapBindingImpl binding2; 67 68 private ObservableMap<Object, Object> emptyMap; 69 private ObservableMap<Object, Object> set1; 70 private ObservableMap<Object, Object> set2; 71 72 private MockMapObserver<Object, Object> listener; 73 74 @Before 75 public void setUp() { 76 dependency1 = new ObservableStub(); 77 dependency2 = new ObservableStub(); 78 binding0 = new MapBindingImpl(); 79 binding1 = new MapBindingImpl(dependency1); 80 binding2 = new MapBindingImpl(dependency1, dependency2); 81 emptyMap = FXCollections.observableMap(Collections.emptyMap()); 82 set1 = FXCollections.observableMap(Collections.singletonMap(KEY_1, DATA_1)); 83 final Map<Object, Object> map = new HashMap<Object, Object>(); 84 map.put(KEY_2_0, DATA_2_0); 85 map.put(KEY_2_1, DATA_2_1); 86 set2 = FXCollections.observableMap(map); 87 listener = new MockMapObserver<Object, Object>(); 88 binding0.setValue(set2); 89 binding1.setValue(set2); 90 binding2.setValue(set2); 91 } 92 93 @Test 94 public void testSizeProperty() { 95 assertEquals(binding0, binding0.sizeProperty().getBean()); 96 assertEquals(binding1, binding1.sizeProperty().getBean()); 97 assertEquals(binding2, binding2.sizeProperty().getBean()); 98 99 final ReadOnlyIntegerProperty size = binding1.sizeProperty(); 100 assertEquals("size", size.getName()); 101 102 assertEquals(2, size.get()); 103 binding1.setValue(emptyMap); 104 dependency1.fireValueChangedEvent(); 105 assertEquals(0, size.get()); 106 binding1.setValue(null); 107 dependency1.fireValueChangedEvent(); 108 assertEquals(0, size.get()); 109 binding1.setValue(set1); 110 dependency1.fireValueChangedEvent(); 111 assertEquals(1, size.get()); 112 } 113 114 @Test 115 public void testEmptyProperty() { 116 assertEquals(binding0, binding0.emptyProperty().getBean()); 117 assertEquals(binding1, binding1.emptyProperty().getBean()); 118 assertEquals(binding2, binding2.emptyProperty().getBean()); 119 120 final ReadOnlyBooleanProperty empty = binding1.emptyProperty(); 121 assertEquals("empty", empty.getName()); 122 123 assertFalse(empty.get()); 124 binding1.setValue(emptyMap); 125 dependency1.fireValueChangedEvent(); 126 assertTrue(empty.get()); 127 binding1.setValue(null); 128 dependency1.fireValueChangedEvent(); 129 assertTrue(empty.get()); 130 binding1.setValue(set1); 131 dependency1.fireValueChangedEvent(); 132 assertFalse(empty.get()); 133 } 134 135 @Test 136 public void testNoDependency_MapChangeListener() { 137 binding0.getValue(); 138 binding0.addListener(listener); 139 System.gc(); // making sure we did not not overdo weak references 140 assertEquals(true, binding0.isValid()); 141 142 // calling getValue() 143 binding0.reset(); 144 binding0.getValue(); 145 assertEquals(0, binding0.getComputeValueCounter()); 146 assertEquals(0, listener.getCallsNumber()); 147 assertEquals(true, binding0.isValid()); 148 } 149 150 @Test 151 public void testSingleDependency_MapChangeListener() { 152 binding1.getValue(); 153 binding1.addListener(listener); 154 System.gc(); // making sure we did not not overdo weak references 155 assertEquals(true, binding1.isValid()); 156 157 // fire single change event 158 binding1.reset(); 159 listener.clear(); 160 binding1.setValue(set1); 161 dependency1.fireValueChangedEvent(); 162 assertEquals(1, binding1.getComputeValueCounter()); 163 listener.assertMultipleCalls(new Call[]{new Call<Object, Object>(KEY_2_0, DATA_2_0, null), new Call<Object, Object>(KEY_2_1, DATA_2_1, null), new Call<Object, Object>(KEY_1, null, DATA_1)}); 164 assertEquals(true, binding1.isValid()); 165 listener.clear(); 166 167 binding1.getValue(); 168 assertEquals(0, binding1.getComputeValueCounter()); 169 assertEquals(0, listener.getCallsNumber()); 170 assertEquals(true, binding1.isValid()); 171 listener.clear(); 172 173 // fire single change event with same value 174 binding1.setValue(set1); 175 dependency1.fireValueChangedEvent(); 176 assertEquals(1, binding1.getComputeValueCounter()); 177 assertEquals(0, listener.getCallsNumber()); 178 assertEquals(true, binding1.isValid()); 179 listener.clear(); 180 181 binding1.getValue(); 182 assertEquals(0, binding1.getComputeValueCounter()); 183 assertEquals(0, listener.getCallsNumber()); 184 assertEquals(true, binding1.isValid()); 185 listener.clear(); 186 187 // fire two change events 188 binding1.setValue(set2); 189 dependency1.fireValueChangedEvent(); 190 listener.clear(); 191 binding1.setValue(set1); 192 dependency1.fireValueChangedEvent(); 193 assertEquals(2, binding1.getComputeValueCounter()); 194 listener.assertMultipleCalls(new Call[]{new Call<Object, Object>(KEY_2_0, DATA_2_0, null), new Call<Object, Object>(KEY_2_1, DATA_2_1, null), new Call<Object, Object>(KEY_1, null, DATA_1)}); 195 assertEquals(true, binding1.isValid()); 196 listener.clear(); 197 198 binding1.getValue(); 199 assertEquals(0, binding1.getComputeValueCounter()); 200 assertEquals(0, listener.getCallsNumber()); 201 assertEquals(true, binding1.isValid()); 202 listener.clear(); 203 204 // fire two change events with same value 205 binding1.setValue(set2); 206 dependency1.fireValueChangedEvent(); 207 binding1.setValue(set2); 208 dependency1.fireValueChangedEvent(); 209 assertEquals(2, binding1.getComputeValueCounter()); 210 listener.assertMultipleCalls(new Call[] {new Call<Object, Object>(KEY_1, DATA_1, null), new Call<Object, Object>(KEY_2_0, null, DATA_2_0), new Call<Object, Object>(KEY_2_1, null, DATA_2_1)}); 211 assertEquals(true, binding1.isValid()); 212 listener.clear(); 213 214 binding1.getValue(); 215 assertEquals(0, binding1.getComputeValueCounter()); 216 assertEquals(0, listener.getCallsNumber()); 217 assertEquals(true, binding1.isValid()); 218 } 219 220 @Test 221 public void testChangeContent_InvalidationListener() { 222 final InvalidationListenerMock listenerMock = new InvalidationListenerMock(); 223 binding1.get(); 224 binding1.addListener(listenerMock); 225 assertTrue(binding1.isValid()); 226 227 binding1.reset(); 228 listenerMock.reset(); 229 set2.put(new Object(), new Object()); 230 assertEquals(0, binding1.getComputeValueCounter()); 231 listenerMock.check(binding1, 1); 232 assertTrue(binding1.isValid()); 233 } 234 235 @Test 236 public void testChangeContent_ChangeListener() { 237 final ChangeListenerMock listenerMock = new ChangeListenerMock(null); 238 binding1.get(); 239 binding1.addListener(listenerMock); 240 assertTrue(binding1.isValid()); 241 242 binding1.reset(); 243 listenerMock.reset(); 244 set2.put(new Object(), new Object()); 245 assertEquals(0, binding1.getComputeValueCounter()); 246 listenerMock.check(binding1, set2, set2, 1); 247 assertTrue(binding1.isValid()); 248 } 249 250 @Test 251 public void testChangeContent_MapChangeListener() { 252 binding1.get(); 253 binding1.addListener(listener); 254 assertTrue(binding1.isValid()); 255 256 final Object newKey = new Object(); 257 final Object newData = new Object(); 258 binding1.reset(); 259 listener.clear(); 260 set2.put(newKey, newData); 261 assertEquals(0, binding1.getComputeValueCounter()); 262 listener.assertAdded(MockMapObserver.Tuple.tup(newKey, newData)); 263 assertTrue(binding1.isValid()); 264 } 265 266 public static class ObservableStub extends ObservableValueBase<Object> { 267 @Override public void fireValueChangedEvent() {super.fireValueChangedEvent();} 268 269 @Override 270 public Object getValue() { 271 return null; 272 } 273 } 274 275 private static class MapBindingImpl extends MapBinding<Object, Object> { 276 277 private int computeValueCounter = 0; 278 private ObservableMap<Object, Object> value; 279 280 public void setValue(ObservableMap<Object, Object> value) { 281 this.value = value; 282 } 283 284 public MapBindingImpl(Observable... dep) { 285 super.bind(dep); 286 } 287 288 public int getComputeValueCounter() { 289 final int result = computeValueCounter; 290 reset(); 291 return result; 292 } 293 294 public void reset() { 295 computeValueCounter = 0; 296 } 297 298 @Override 299 public ObservableMap<Object, Object> computeValue() { 300 computeValueCounter++; 301 return value; 302 } 303 304 @Override @ReturnsUnmodifiableCollection 305 public ObservableList<?> getDependencies() { 306 fail("Should not reach here"); 307 return null; 308 } 309 } 310 311 312 // private class MapChangeListenerMock implements MapChangeListener<Object, Object> { 313 // 314 // private Change<? extends Object> change; 315 // private int counter; 316 // 317 // @Override 318 // public void onChanged(Change<? extends Object> change) { 319 // this.change = change; 320 // counter++; 321 // } 322 // 323 // private void reset() { 324 // change = null; 325 // counter = 0; 326 // } 327 // 328 // private void checkNotCalled() { 329 // assertEquals(null, change); 330 // assertEquals(0, counter); 331 // reset(); 332 // } 333 // 334 // private void check(ObservableMap<Object, Object> oldMap, ObservableMap<Object, Object> newMap, int counter) { 335 // assertTrue(change.next()); 336 // assertTrue(change.wasReplaced()); 337 // assertEquals(oldMap, change.getRemoved()); 338 // assertEquals(newMap, change.getMap()); 339 // assertFalse(change.next()); 340 // assertEquals(counter, this.counter); 341 // reset(); 342 // } 343 // 344 // private void check(int pos, Object newObject, int counter) { 345 // assertTrue(change.next()); 346 // assertTrue(change.wasAdded()); 347 // assertEquals(pos, change.getFrom()); 348 // assertEquals(Collections.singletonMap(newObject), change.getAddedSubMap()); 349 // assertFalse(change.next()); 350 // assertEquals(counter, this.counter); 351 // reset(); 352 // } 353 // } 354 }