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 com.sun.javafx.binding; 27 28 import javafx.beans.InvalidationListener; 29 import javafx.beans.binding.Bindings; 30 import javafx.beans.property.*; 31 import javafx.beans.value.ChangeListener; 32 import org.junit.Before; 33 import org.junit.Test; 34 import org.junit.runner.RunWith; 35 import org.junit.runners.Parameterized; 36 37 import java.util.Arrays; 38 import java.util.Collection; 39 40 import static org.junit.Assert.*; 41 import org.junit.Rule; 42 import org.junit.rules.ExpectedException; 43 44 @RunWith(Parameterized.class) 45 public class BidirectionalBindingTest<T> { 46 47 private static final float EPSILON_FLOAT = 1e-5f; 48 private static final double EPSILON_DOUBLE = 1e-10; 49 50 public static interface Functions<S> { 51 PropertyMock<S> create(); 52 void bind(PropertyMock<S> obj1, PropertyMock<S> obj2); 53 void unbind(PropertyMock<S> obj1, PropertyMock<S> obj2); 54 BidirectionalBinding createBindingDirectly(PropertyMock<S> op1, PropertyMock<S> op2); 55 void check(S expected, S actual); 56 } 57 58 private final Functions<T> func; 59 private final T[] v; 60 61 @Rule 62 public ExpectedException thrown = ExpectedException.none(); 63 64 private PropertyMock<T> op1; 65 private PropertyMock<T> op2; 66 private PropertyMock<T> op3; 67 private PropertyMock<T> op4; 68 69 public BidirectionalBindingTest(Functions<T> func, T[] v) { 70 this.op1 = func.create(); 71 this.op2 = func.create(); 72 this.op3 = func.create(); 73 this.op4 = func.create(); 74 this.func = func; 75 this.v = v; 76 } 77 78 @Before 79 public void setUp() { 80 op1.setValue(v[0]); 81 op2.setValue(v[1]); 82 } 83 84 @Test 85 public void testBind() { 86 func.bind(op1, op2); 87 System.gc(); // making sure we did not not overdo weak references 88 func.check(v[1], op1.getValue()); 89 func.check(v[1], op2.getValue()); 90 91 op1.setValue(v[2]); 92 func.check(v[2], op1.getValue()); 93 func.check(v[2], op2.getValue()); 94 95 op2.setValue(v[3]); 96 func.check(v[3], op1.getValue()); 97 func.check(v[3], op2.getValue()); 98 } 99 100 @Test 101 public void testUnbind() { 102 // unbind non-existing binding => no-op 103 func.unbind(op1, op2); 104 105 // unbind properties of different beans 106 func.bind(op1, op2); 107 System.gc(); // making sure we did not not overdo weak references 108 func.check(v[1], op1.getValue()); 109 func.check(v[1], op2.getValue()); 110 111 func.unbind(op1, op2); 112 System.gc(); 113 func.check(v[1], op1.getValue()); 114 func.check(v[1], op2.getValue()); 115 116 op1.setValue(v[2]); 117 func.check(v[2], op1.getValue()); 118 func.check(v[1], op2.getValue()); 119 120 op2.setValue(v[3]); 121 func.check(v[2], op1.getValue()); 122 func.check(v[3], op2.getValue()); 123 } 124 125 @Test 126 public void testChaining() { 127 op3.setValue(v[2]); 128 func.bind(op1, op2); 129 func.bind(op2, op3); 130 System.gc(); // making sure we did not not overdo weak references 131 func.check(v[2], op1.getValue()); 132 func.check(v[2], op2.getValue()); 133 func.check(v[2], op3.getValue()); 134 135 op1.setValue(v[3]); 136 func.check(v[3], op1.getValue()); 137 func.check(v[3], op2.getValue()); 138 func.check(v[3], op3.getValue()); 139 140 op2.setValue(v[0]); 141 func.check(v[0], op1.getValue()); 142 func.check(v[0], op2.getValue()); 143 func.check(v[0], op3.getValue()); 144 145 op3.setValue(v[1]); 146 func.check(v[1], op1.getValue()); 147 func.check(v[1], op2.getValue()); 148 func.check(v[1], op3.getValue()); 149 150 // now unbind 151 func.unbind(op1, op2); 152 System.gc(); // making sure we did not not overdo weak references 153 func.check(v[1], op1.getValue()); 154 func.check(v[1], op2.getValue()); 155 func.check(v[1], op3.getValue()); 156 157 op1.setValue(v[2]); 158 func.check(v[2], op1.getValue()); 159 func.check(v[1], op2.getValue()); 160 func.check(v[1], op3.getValue()); 161 162 op2.setValue(v[3]); 163 func.check(v[2], op1.getValue()); 164 func.check(v[3], op2.getValue()); 165 func.check(v[3], op3.getValue()); 166 167 op3.setValue(v[0]); 168 func.check(v[2], op1.getValue()); 169 func.check(v[0], op2.getValue()); 170 func.check(v[0], op3.getValue()); 171 } 172 173 @Test 174 public void testWeakReferencing() { 175 func.bind(op1, op2); 176 assertEquals(1, op1.getListenerCount()); 177 assertEquals(1, op2.getListenerCount()); 178 179 op1 = null; 180 System.gc(); 181 op2.setValue(v[2]); 182 assertEquals(0, op2.getListenerCount()); 183 184 func.bind(op2, op3); 185 assertEquals(1, op2.getListenerCount()); 186 assertEquals(1, op3.getListenerCount()); 187 188 op3 = null; 189 System.gc(); 190 op2.setValue(v[0]); 191 assertEquals(0, op2.getListenerCount()); 192 } 193 194 @Test 195 public void testHashCode() { 196 final int hc1 = func.createBindingDirectly(op1, op2).hashCode(); 197 final int hc2 = func.createBindingDirectly(op2, op1).hashCode(); 198 assertEquals(hc1, hc2); 199 } 200 201 202 @Test 203 public void testEquals() { 204 final BidirectionalBinding golden = func.createBindingDirectly(op1, op2); 205 206 assertTrue(golden.equals(golden)); 207 assertFalse(golden.equals(null)); 208 assertFalse(golden.equals(op1)); 209 assertTrue(golden.equals(func.createBindingDirectly(op1, op2))); 210 assertTrue(golden.equals(func.createBindingDirectly(op2, op1))); 211 assertFalse(golden.equals(func.createBindingDirectly(op1, op3))); 212 assertFalse(golden.equals(func.createBindingDirectly(op3, op1))); 213 assertFalse(golden.equals(func.createBindingDirectly(op3, op2))); 214 assertFalse(golden.equals(func.createBindingDirectly(op2, op3))); 215 } 216 217 @Test 218 public void testEqualsWithGCedProperty() { 219 final BidirectionalBinding binding1 = func.createBindingDirectly(op1, op2); 220 final BidirectionalBinding binding2 = func.createBindingDirectly(op1, op2); 221 final BidirectionalBinding binding3 = func.createBindingDirectly(op2, op1); 222 final BidirectionalBinding binding4 = func.createBindingDirectly(op2, op1); 223 op1 = null; 224 System.gc(); 225 226 assertTrue(binding1.equals(binding1)); 227 assertFalse(binding1.equals(binding2)); 228 assertFalse(binding1.equals(binding3)); 229 230 assertTrue(binding3.equals(binding3)); 231 assertFalse(binding3.equals(binding1)); 232 assertFalse(binding3.equals(binding4)); 233 } 234 235 @Test(expected=NullPointerException.class) 236 public void testBind_Null_X() { 237 func.bind(null, op2); 238 } 239 240 @Test(expected=NullPointerException.class) 241 public void testBind_X_Null() { 242 func.bind(op1, null); 243 } 244 245 @Test(expected=IllegalArgumentException.class) 246 public void testBind_X_Self() { 247 func.bind(op1, op1); 248 } 249 250 @Test(expected=NullPointerException.class) 251 public void testUnbind_Null_X() { 252 func.unbind(null, op2); 253 } 254 255 @Test(expected=NullPointerException.class) 256 public void testUnbind_X_Null() { 257 func.unbind(op1, null); 258 } 259 260 @Test(expected=IllegalArgumentException.class) 261 public void testUnbind_X_Self() { 262 func.unbind(op1, op1); 263 } 264 265 @Test 266 public void testBrokenBind() { 267 func.bind(op1, op2); 268 op1.bind(op3); 269 assertEquals(op3.getValue(), op1.getValue()); 270 assertEquals(op2.getValue(), op1.getValue()); 271 272 op2.setValue(v[2]); 273 assertEquals(op3.getValue(), op1.getValue()); 274 assertEquals(op2.getValue(), op1.getValue()); 275 } 276 277 @Test 278 public void testDoubleBrokenBind() { 279 func.bind(op1, op2); 280 op1.bind(op3); 281 op4.setValue(v[0]); 282 283 op2.bind(op4); 284 assertEquals(op4.getValue(), op2.getValue()); 285 assertEquals(op3.getValue(), op1.getValue()); 286 // Test that bidirectional binding was unbound in this case 287 op3.setValue(v[0]); 288 op4.setValue(v[1]); 289 assertEquals(op4.getValue(), op2.getValue()); 290 assertEquals(op3.getValue(), op1.getValue()); 291 assertEquals(v[0], op1.getValue()); 292 assertEquals(v[1], op2.getValue()); 293 } 294 295 @Parameterized.Parameters 296 public static Collection<Object[]> parameters() { 297 final Boolean[] booleanData = new Boolean[] {true, false, true, false}; 298 final Double[] doubleData = new Double[] {2348.2345, -92.214, -214.0214, -908.214}; 299 final Float[] floatData = new Float[] {-3592.9f, 234872.8347f, 3897.274f, 3958.938745f}; 300 final Integer[] integerData = new Integer[] {248, -9384, -234, -34}; 301 final Long[] longData = new Long[] {9823984L, 2908934L, -234234L, 9089234L}; 302 final Object[] objectData = new Object[] {new Object(), new Object(), new Object(), new Object()}; 303 final String[] stringData = new String[] {"A", "B", "C", "D"}; 304 305 return Arrays.asList(new Object[][] { 306 // boolean 307 { 308 new Functions<Boolean>() { 309 @Override 310 public PropertyMock<Boolean> create() { 311 return new BooleanPropertyMock(); 312 } 313 @Override 314 public void bind(PropertyMock<Boolean> op1, PropertyMock<Boolean> op2) { 315 Bindings.bindBidirectional(op1, op2); 316 } 317 @Override 318 public void unbind(PropertyMock<Boolean> op1, PropertyMock<Boolean> op2) { 319 Bindings.unbindBidirectional(op1, op2); 320 } 321 @Override 322 public BidirectionalBinding createBindingDirectly(PropertyMock<Boolean> op1, PropertyMock<Boolean> op2) { 323 return BidirectionalBinding.bind(op1, op2); 324 } 325 @Override 326 public void check(Boolean expected, Boolean actual) { 327 assertEquals(expected, actual); 328 } 329 }, 330 booleanData 331 }, 332 // double 333 { 334 new Functions<Number>() { 335 @Override 336 public PropertyMock<Number> create() { 337 return new DoublePropertyMock(); 338 } 339 @Override 340 public void bind(PropertyMock<Number> op1, PropertyMock<Number> op2) { 341 Bindings.bindBidirectional(op1, op2); 342 } 343 @Override 344 public void unbind(PropertyMock<Number> op1, PropertyMock<Number> op2) { 345 Bindings.unbindBidirectional(op1, op2); 346 } 347 @Override 348 public BidirectionalBinding createBindingDirectly(PropertyMock<Number> op1, PropertyMock<Number> op2) { 349 return BidirectionalBinding.bind(op1, op2); 350 } 351 @Override 352 public void check(Number expected, Number actual) { 353 assertEquals(expected.doubleValue(), actual.doubleValue(), EPSILON_DOUBLE); 354 } 355 }, 356 doubleData 357 }, 358 // float 359 { 360 new Functions<Number>() { 361 @Override 362 public PropertyMock<Number> create() { 363 return new FloatPropertyMock(); 364 } 365 @Override 366 public void bind(PropertyMock<Number> op1, PropertyMock<Number> op2) { 367 Bindings.bindBidirectional(op1, op2); 368 } 369 @Override 370 public void unbind(PropertyMock<Number> op1, PropertyMock<Number> op2) { 371 Bindings.unbindBidirectional(op1, op2); 372 } 373 @Override 374 public BidirectionalBinding createBindingDirectly(PropertyMock<Number> op1, PropertyMock<Number> op2) { 375 return BidirectionalBinding.bind(op1, op2); 376 } 377 @Override 378 public void check(Number expected, Number actual) { 379 assertEquals(expected.floatValue(), actual.floatValue(), EPSILON_FLOAT); 380 } 381 }, 382 floatData 383 }, 384 // integer 385 { 386 new Functions<Number>() { 387 @Override 388 public PropertyMock<Number> create() { 389 return new IntegerPropertyMock(); 390 } 391 @Override 392 public void bind(PropertyMock<Number> op1, PropertyMock<Number> op2) { 393 Bindings.bindBidirectional(op1, op2); 394 } 395 @Override 396 public void unbind(PropertyMock<Number> op1, PropertyMock<Number> op2) { 397 Bindings.unbindBidirectional(op1, op2); 398 } 399 @Override 400 public BidirectionalBinding createBindingDirectly(PropertyMock<Number> op1, PropertyMock<Number> op2) { 401 return BidirectionalBinding.bind(op1, op2); 402 } 403 @Override 404 public void check(Number expected, Number actual) { 405 assertEquals(expected.intValue(), actual.intValue()); 406 } 407 }, 408 integerData 409 }, 410 // long 411 { 412 new Functions<Number>() { 413 @Override 414 public PropertyMock<Number> create() { 415 return new LongPropertyMock(); 416 } 417 @Override 418 public void bind(PropertyMock<Number> op1, PropertyMock<Number> op2) { 419 Bindings.bindBidirectional(op1, op2); 420 } 421 @Override 422 public void unbind(PropertyMock<Number> op1, PropertyMock<Number> op2) { 423 Bindings.unbindBidirectional(op1, op2); 424 } 425 @Override 426 public BidirectionalBinding createBindingDirectly(PropertyMock<Number> op1, PropertyMock<Number> op2) { 427 return BidirectionalBinding.bind(op1, op2); 428 } 429 @Override 430 public void check(Number expected, Number actual) { 431 assertEquals(expected.longValue(), actual.longValue()); 432 } 433 }, 434 longData 435 }, 436 // object 437 { 438 new Functions<Object>() { 439 @Override 440 public PropertyMock<Object> create() { 441 return new ObjectPropertyMock<Object>(); 442 } 443 @Override 444 public void bind(PropertyMock<Object> op1, PropertyMock<Object> op2) { 445 Bindings.bindBidirectional(op1, op2); 446 } 447 @Override 448 public void unbind(PropertyMock<Object> op1, PropertyMock<Object> op2) { 449 Bindings.unbindBidirectional(op1, op2); 450 } 451 @Override 452 public BidirectionalBinding createBindingDirectly(PropertyMock<Object> op1, PropertyMock<Object> op2) { 453 return BidirectionalBinding.bind(op1, op2); 454 } 455 @Override 456 public void check(Object expected, Object actual) { 457 assertEquals(expected, actual); 458 } 459 }, 460 objectData 461 }, 462 // string 463 { 464 new Functions<String>() { 465 @Override 466 public PropertyMock<String> create() { 467 return new StringPropertyMock(); 468 } 469 @Override 470 public void bind(PropertyMock<String> op1, PropertyMock<String> op2) { 471 Bindings.bindBidirectional(op1, op2); 472 } 473 @Override 474 public void unbind(PropertyMock<String> op1, PropertyMock<String> op2) { 475 Bindings.unbindBidirectional(op1, op2); 476 } 477 @Override 478 public BidirectionalBinding createBindingDirectly(PropertyMock<String> op1, PropertyMock<String> op2) { 479 return BidirectionalBinding.bind(op1, op2); 480 } 481 @Override 482 public void check(String expected, String actual) { 483 assertEquals(expected, actual); 484 } 485 }, 486 stringData 487 }, 488 }); 489 } 490 491 private interface PropertyMock<T> extends Property<T> { 492 int getListenerCount(); 493 } 494 495 private static class BooleanPropertyMock extends SimpleBooleanProperty implements PropertyMock<Boolean> { 496 497 private int listenerCount = 0; 498 499 @Override 500 public int getListenerCount() { 501 return listenerCount; 502 } 503 504 @Override 505 public void addListener(ChangeListener<? super Boolean> listener) { 506 super.addListener(listener); 507 listenerCount++; 508 } 509 510 @Override 511 public void removeListener(ChangeListener<? super Boolean> listener) { 512 super.removeListener(listener); 513 listenerCount--; 514 } 515 } 516 517 private static class DoublePropertyMock extends SimpleDoubleProperty implements PropertyMock<Number> { 518 519 private int listenerCount = 0; 520 521 @Override 522 public int getListenerCount() { 523 return listenerCount; 524 } 525 526 @Override 527 public void addListener(ChangeListener<? super Number> listener) { 528 super.addListener(listener); 529 listenerCount++; 530 } 531 532 @Override 533 public void removeListener(ChangeListener<? super Number> listener) { 534 super.removeListener(listener); 535 listenerCount--; 536 } 537 } 538 539 private static class FloatPropertyMock extends SimpleFloatProperty implements PropertyMock<Number> { 540 541 private int listenerCount = 0; 542 543 @Override 544 public int getListenerCount() { 545 return listenerCount; 546 } 547 548 @Override 549 public void addListener(ChangeListener<? super Number> listener) { 550 super.addListener(listener); 551 listenerCount++; 552 } 553 554 @Override 555 public void removeListener(ChangeListener<? super Number> listener) { 556 super.removeListener(listener); 557 listenerCount--; 558 } 559 } 560 561 private static class IntegerPropertyMock extends SimpleIntegerProperty implements PropertyMock<Number> { 562 563 private int listenerCount = 0; 564 565 @Override 566 public int getListenerCount() { 567 return listenerCount; 568 } 569 570 @Override 571 public void addListener(ChangeListener<? super Number> listener) { 572 super.addListener(listener); 573 listenerCount++; 574 } 575 576 @Override 577 public void removeListener(ChangeListener<? super Number> listener) { 578 super.removeListener(listener); 579 listenerCount--; 580 } 581 } 582 583 private static class LongPropertyMock extends SimpleLongProperty implements PropertyMock<Number> { 584 585 private int listenerCount = 0; 586 587 @Override 588 public int getListenerCount() { 589 return listenerCount; 590 } 591 592 @Override 593 public void addListener(ChangeListener<? super Number> listener) { 594 super.addListener(listener); 595 listenerCount++; 596 } 597 598 @Override 599 public void removeListener(ChangeListener<? super Number> listener) { 600 super.removeListener(listener); 601 listenerCount--; 602 } 603 } 604 605 private static class ObjectPropertyMock<T> extends SimpleObjectProperty<T> implements PropertyMock<T> { 606 607 private int listenerCount = 0; 608 609 @Override 610 public int getListenerCount() { 611 return listenerCount; 612 } 613 614 @Override 615 public void addListener(ChangeListener<? super T> listener) { 616 super.addListener(listener); 617 listenerCount++; 618 } 619 620 @Override 621 public void removeListener(ChangeListener<? super T> listener) { 622 super.removeListener(listener); 623 listenerCount--; 624 } 625 } 626 627 private static class StringPropertyMock extends SimpleStringProperty implements PropertyMock<String> { 628 629 private int listenerCount = 0; 630 631 @Override 632 public int getListenerCount() { 633 return listenerCount; 634 } 635 636 @Override 637 public void addListener(ChangeListener<? super String> listener) { 638 super.addListener(listener); 639 listenerCount++; 640 } 641 642 @Override 643 public void removeListener(ChangeListener<? super String> listener) { 644 super.removeListener(listener); 645 listenerCount--; 646 } 647 } 648 }