1 /* 2 * Copyright (c) 2010, 2018, 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 package test.com.sun.javafx.binding; 26 27 import com.sun.javafx.binding.Logging; 28 import java.beans.PropertyChangeListener; 29 import java.beans.PropertyChangeSupport; 30 import java.util.ArrayList; 31 import java.util.Arrays; 32 import java.util.List; 33 import java.util.Random; 34 import test.javafx.beans.Person; 35 import javafx.beans.binding.Bindings; 36 import javafx.beans.binding.BooleanBinding; 37 import javafx.beans.binding.DoubleBinding; 38 import javafx.beans.binding.FloatBinding; 39 import javafx.beans.binding.IntegerBinding; 40 import javafx.beans.binding.LongBinding; 41 import javafx.beans.binding.ObjectBinding; 42 import javafx.beans.binding.StringBinding; 43 import test.javafx.binding.Variable; 44 import javafx.collections.ObservableList; 45 import com.sun.javafx.logging.PlatformLogger.Level; 46 import org.junit.AfterClass; 47 import org.junit.Before; 48 import org.junit.BeforeClass; 49 import org.junit.Test; 50 51 import static org.junit.Assert.*; 52 53 public class SelectBindingTest { 54 55 private static final double EPSILON_DOUBLE = 1e-12; 56 private static final float EPSILON_FLOAT = 1e-6f; 57 58 public static class POJOPerson { 59 60 private String name; 61 62 public String getName() { 63 return name; 64 } 65 66 public POJOPerson() { 67 } 68 69 public POJOPerson(String name) { 70 this.name = name; 71 } 72 73 } 74 75 public static class POJONext { 76 77 private Object next; 78 private PropertyChangeSupport pcs = new PropertyChangeSupport(this); 79 80 public Object getNext() { 81 return next; 82 } 83 84 public void setNext(Object next) { 85 Object old = this.next; 86 this.next = next; 87 pcs.firePropertyChange("next", old, next); 88 } 89 90 public void addPropertyChangeListener(String property, PropertyChangeListener pcl) { 91 pcs.addPropertyChangeListener(property, pcl); 92 } 93 94 public void removePropertyChangeListener(String property, PropertyChangeListener pcl) { 95 pcs.removePropertyChangeListener(property, pcl); 96 } 97 98 } 99 100 private Variable a; 101 private Variable b; 102 private Variable c; 103 private Variable d; 104 private StringBinding select; 105 private ObservableList<?> dependencies; 106 107 private static final ErrorLoggingUtiltity log = new ErrorLoggingUtiltity(); 108 109 @BeforeClass 110 public static void setUpClass() { 111 System.err.println("SelectBindingTest : log messages are expected from these tests."); 112 log.start(); 113 } 114 115 @AfterClass 116 public static void tearDownClass() { 117 log.stop(); 118 } 119 120 @Before 121 public void setUp() throws Exception { 122 a = new Variable("a"); 123 b = new Variable("b"); 124 c = new Variable("c"); 125 d = new Variable("d"); 126 a.setNext(b); 127 b.setNext(c); 128 select = Bindings.selectString(a.nextProperty(), "next", "name"); 129 dependencies = select.getDependencies(); 130 } 131 132 @Test 133 public void testObject() { 134 final Person person1 = new Person(); 135 final Person person2 = new Person(); 136 c.setNext(person1); 137 final ObjectBinding<Object> objectBinding = Bindings.select(a.nextProperty(), "next", "next"); 138 assertEquals(person1, objectBinding.get()); 139 c.setNext(person2); 140 assertEquals(person2, objectBinding.get()); 141 b.setNext(null); 142 assertEquals(null, objectBinding.get()); 143 log.checkFine(NullPointerException.class); 144 } 145 146 @Test 147 public void testPOJOObject() { 148 POJONext pojoA = new POJONext(); 149 pojoA.setNext(b); 150 final Person person1 = new Person(); 151 final Person person2 = new Person(); 152 c.setNext(person1); 153 final ObjectBinding<Object> objectBinding = Bindings.select(pojoA, "next", "next", "next"); 154 assertEquals(person1, objectBinding.get()); 155 c.setNext(person2); 156 assertEquals(person2, objectBinding.get()); 157 } 158 159 @Test 160 public void testPOJOObject_2() { 161 POJONext pojoC = new POJONext(); 162 b.setNext(pojoC); 163 final Person person1 = new Person(); 164 final Person person2 = new Person(); 165 pojoC.setNext(person1); 166 final ObjectBinding<Object> objectBinding = Bindings.select(a.nextProperty(), "next", "next"); 167 assertEquals(person1, objectBinding.get()); 168 pojoC.setNext(person2); 169 assertEquals(person2, objectBinding.get()); 170 } 171 172 @Test 173 public void testPOJOObject_3() { 174 final POJOPerson person1 = new POJOPerson("P1"); 175 final POJOPerson person2 = new POJOPerson("P2"); 176 c.setNext(person1); 177 final ObjectBinding<Object> objectBinding = Bindings.select(a.nextProperty(), "next", "next", "name"); 178 assertEquals("P1", objectBinding.get()); 179 c.setNext(person2); 180 assertEquals("P2", objectBinding.get()); 181 b.setNext(null); 182 assertEquals(null, objectBinding.get()); 183 log.checkFine(NullPointerException.class); 184 } 185 186 @Test 187 public void testPOJOBoolean() { 188 189 POJONext pojoA = new POJONext(); 190 pojoA.setNext(b); 191 192 final Person person = new Person(); 193 b.setNext(person); 194 final BooleanBinding binding1 = Bindings.selectBoolean(pojoA, "next", "next", "retired"); 195 assertEquals(false, binding1.get()); 196 person.setRetired(true); 197 assertEquals(true, binding1.get()); 198 } 199 200 @Test 201 public void testBoolean() { 202 203 final Person person = new Person(); 204 b.setNext(person); 205 final BooleanBinding binding1 = Bindings.selectBoolean(a.nextProperty(), "next", "retired"); 206 assertEquals(false, binding1.get()); 207 person.setRetired(true); 208 assertEquals(true, binding1.get()); 209 b.setNext(null); 210 assertEquals(false, binding1.get()); 211 log.checkFine(NullPointerException.class); 212 213 person.setData(false); 214 b.setNext(person); 215 final BooleanBinding binding2 = Bindings.selectBoolean(a.nextProperty(), "next", "data"); 216 assertEquals(false, binding2.get()); 217 person.setData(true); 218 assertEquals(true, binding2.get()); 219 person.setData(null); 220 assertEquals(false, binding2.get()); 221 log.checkFine(NullPointerException.class); 222 b.setNext(null); 223 assertEquals(false, binding2.get()); 224 log.checkFine(NullPointerException.class); 225 } 226 227 @Test 228 public void testPOJODouble() { 229 POJONext pojoA = new POJONext(); 230 pojoA.setNext(b); 231 232 final Person person = new Person(); 233 person.setSomething(-Math.E); 234 b.setNext(person); 235 final DoubleBinding binding1 = Bindings.selectDouble(pojoA, "next", "next", "something"); 236 assertEquals(-Math.E, binding1.get(), EPSILON_DOUBLE); 237 person.setSomething(Math.PI); 238 assertEquals(Math.PI, binding1.get(), EPSILON_DOUBLE); 239 } 240 241 @Test 242 public void testDouble() { 243 244 final Person person = new Person(); 245 person.setSomething(-Math.E); 246 b.setNext(person); 247 final DoubleBinding binding1 = Bindings.selectDouble(a.nextProperty(), "next", "something"); 248 assertEquals(-Math.E, binding1.get(), EPSILON_DOUBLE); 249 person.setSomething(Math.PI); 250 assertEquals(Math.PI, binding1.get(), EPSILON_DOUBLE); 251 b.setNext(null); 252 assertEquals(0.0, binding1.get(), EPSILON_DOUBLE); 253 log.checkFine(NullPointerException.class); 254 255 person.setData(-Math.E); 256 b.setNext(person); 257 final DoubleBinding binding2 = Bindings.selectDouble(a.nextProperty(), "next", "data"); 258 assertEquals(-Math.E, binding2.get(), EPSILON_DOUBLE); 259 person.setData(Math.PI); 260 assertEquals(Math.PI, binding2.get(), EPSILON_DOUBLE); 261 person.setData(null); 262 assertEquals(0.0, binding2.get(), EPSILON_DOUBLE); 263 log.checkFine(NullPointerException.class); 264 b.setNext(null); 265 assertEquals(0.0, binding2.get(), EPSILON_DOUBLE); 266 log.checkFine(NullPointerException.class); 267 } 268 269 @Test 270 public void testPOJOFloat() { 271 POJONext pojoA = new POJONext(); 272 pojoA.setNext(b); 273 274 final Person person = new Person(); 275 person.setMiles((float) -Math.E); 276 b.setNext(person); 277 final FloatBinding binding1 = Bindings.selectFloat(pojoA, "next", "next", "miles"); 278 assertEquals((float) -Math.E, binding1.get(), EPSILON_FLOAT); 279 person.setMiles((float) Math.PI); 280 assertEquals((float) Math.PI, binding1.get(), EPSILON_FLOAT); 281 } 282 283 @Test 284 public void testFloat() { 285 286 final Person person = new Person(); 287 person.setMiles((float) -Math.E); 288 b.setNext(person); 289 final FloatBinding binding1 = Bindings.selectFloat(a.nextProperty(), "next", "miles"); 290 assertEquals((float) -Math.E, binding1.get(), EPSILON_FLOAT); 291 person.setMiles((float) Math.PI); 292 assertEquals((float) Math.PI, binding1.get(), EPSILON_FLOAT); 293 b.setNext(null); 294 assertEquals(0.0f, binding1.get(), EPSILON_FLOAT); 295 log.checkFine(NullPointerException.class); 296 297 person.setData((float) -Math.E); 298 b.setNext(person); 299 final FloatBinding binding2 = Bindings.selectFloat(a.nextProperty(), "next", "data"); 300 assertEquals((float) -Math.E, binding2.get(), EPSILON_FLOAT); 301 person.setData((float) Math.PI); 302 assertEquals((float) Math.PI, binding2.get(), EPSILON_FLOAT); 303 person.setData(null); 304 assertEquals(0.0f, binding2.get(), EPSILON_FLOAT); 305 log.checkFine(NullPointerException.class); 306 b.setNext(null); 307 assertEquals(0.0f, binding2.get(), EPSILON_FLOAT); 308 log.checkFine(NullPointerException.class); 309 } 310 311 @Test 312 public void testPOJOInteger() { 313 314 POJONext pojoA = new POJONext(); 315 pojoA.setNext(b); 316 317 final Person person = new Person(); 318 person.setAge(42); 319 b.setNext(person); 320 final IntegerBinding binding1 = Bindings.selectInteger(pojoA, "next", "next", "age"); 321 assertEquals(42, binding1.get()); 322 person.setAge(-18); 323 assertEquals(-18, binding1.get()); 324 } 325 326 @Test 327 public void testInteger() { 328 329 final Person person = new Person(); 330 person.setAge(42); 331 b.setNext(person); 332 final IntegerBinding binding1 = Bindings.selectInteger(a.nextProperty(), "next", "age"); 333 assertEquals(42, binding1.get()); 334 person.setAge(-18); 335 assertEquals(-18, binding1.get()); 336 b.setNext(null); 337 assertEquals(0, binding1.get()); 338 log.checkFine(NullPointerException.class); 339 340 person.setData(42); 341 b.setNext(person); 342 final IntegerBinding binding2 = Bindings.selectInteger(a.nextProperty(), "next", "data"); 343 assertEquals(42, binding2.get()); 344 person.setData(-18); 345 assertEquals(-18, binding2.get()); 346 person.setData(null); 347 assertEquals(0, binding2.get()); 348 log.checkFine(NullPointerException.class); 349 b.setNext(null); 350 assertEquals(0, binding2.get()); 351 log.checkFine(NullPointerException.class); 352 } 353 354 @Test 355 public void testPOJOLong() { 356 POJONext pojoA = new POJONext(); 357 pojoA.setNext(b); 358 final Person person = new Person(); 359 person.setIncome(1234567890987654321L); 360 b.setNext(person); 361 final LongBinding binding1 = Bindings.selectLong(pojoA, "next", "next", "income"); 362 assertEquals(1234567890987654321L, binding1.get()); 363 person.setIncome(-987654321234567890L); 364 assertEquals(-987654321234567890L, binding1.get()); 365 } 366 367 @Test 368 public void testLong() { 369 final Person person = new Person(); 370 person.setIncome(1234567890987654321L); 371 b.setNext(person); 372 final LongBinding binding1 = Bindings.selectLong(a.nextProperty(), "next", "income"); 373 assertEquals(1234567890987654321L, binding1.get()); 374 person.setIncome(-987654321234567890L); 375 assertEquals(-987654321234567890L, binding1.get()); 376 b.setNext(null); 377 assertEquals(0L, binding1.get()); 378 log.checkFine(NullPointerException.class); 379 380 person.setData(1234567890987654321L); 381 b.setNext(person); 382 final LongBinding binding2 = Bindings.selectLong(a.nextProperty(), "next", "data"); 383 assertEquals(1234567890987654321L, binding2.get()); 384 person.setData(-987654321234567890L); 385 assertEquals(-987654321234567890L, binding2.get()); 386 person.setData(null); 387 assertEquals(0L, binding2.get()); 388 log.checkFine(NullPointerException.class); 389 b.setNext(null); 390 assertEquals(0L, binding2.get()); 391 log.checkFine(NullPointerException.class); 392 } 393 394 @Test(expected = NullPointerException.class) 395 public void createWithRootNull() { 396 select = Bindings.selectString(null, "next", "name"); 397 } 398 399 @Test 400 public void createWithNoSteps() { 401 select = Bindings.selectString(a.nameProperty()); 402 assertEquals("a", select.get()); 403 a.setName("b"); 404 assertEquals("b", select.get()); 405 a.setName(null); 406 assertNull(select.get()); 407 log.check(java.util.logging.Level.WARNING, NullPointerException.class); 408 } 409 410 @Test(expected = NullPointerException.class) 411 public void createWithOneStepIsNull() { 412 select = Bindings.selectString(a.nextProperty(), null, "name"); 413 } 414 415 @Test 416 public void testNullIsReturnedFromAChainWithAPropertyThatIsNotOnTheAvailableObject() { 417 select = Bindings.selectString(a.nextProperty(), "dummy", "name"); 418 assertNull(select.get()); 419 log.check(java.util.logging.Level.WARNING, NoSuchMethodException.class); 420 } 421 422 @SuppressWarnings("unchecked") 423 @Test 424 public void testAllMembersHaveListeners() { 425 // As yet, there should still be no listeners registered 426 assertEquals(1, a.numChangedListenersForNext); 427 assertEquals(0, a.numChangedListenersForName); 428 assertEquals(0, b.numChangedListenersForNext); 429 assertEquals(0, b.numChangedListenersForName); 430 assertEquals(0, c.numChangedListenersForNext); 431 assertEquals(0, c.numChangedListenersForName); 432 assertEquals(Arrays.asList(a.nextProperty()), dependencies); 433 // Read the value of the select. It should be == "c" 434 assertEquals("c", select.get()); 435 // Now there should be changed listeners for a.value and 436 // b.value and c.name 437 assertEquals(1, a.numChangedListenersForNext); 438 assertEquals(0, a.numChangedListenersForName); 439 assertEquals(1, b.numChangedListenersForNext); 440 assertEquals(0, b.numChangedListenersForName); 441 assertEquals(0, c.numChangedListenersForNext); 442 assertEquals(1, c.numChangedListenersForName); 443 assertEquals(Arrays.asList(a.nextProperty(), b.nextProperty(), c.nameProperty()), dependencies); 444 } 445 446 @SuppressWarnings("unchecked") 447 @Test 448 public void testWhenAValidChangeIsBrokenBySettingTheRootToNullThenTheValueIsNull() { 449 450 assertEquals("c", select.get()); 451 a.setNext(null); 452 assertEquals(Arrays.asList(a.nextProperty()), dependencies); 453 assertNull(select.get()); 454 assertEquals(Arrays.asList(a.nextProperty()), dependencies); 455 log.checkFine(NullPointerException.class); 456 } 457 458 @SuppressWarnings("unchecked") 459 @Test 460 public void testWhenAnIncompleteChainIsMadeCompleteThatTheValueIsComputedCorrectly() { 461 462 a.setNext(null); 463 select.get(); // force it to be validated, for fun 464 log.reset(); 465 a.setNext(b); 466 assertEquals(Arrays.asList(a.nextProperty()), dependencies); 467 assertEquals("c", select.get()); 468 assertEquals(Arrays.asList(a.nextProperty(), b.nextProperty(), c.nameProperty()), dependencies); 469 assertTrue(log.isEmpty()); 470 } 471 472 @SuppressWarnings("unchecked") 473 @Test 474 public void testWhenAValidChangeIsBrokenBySettingTheMiddleToNullThenTheValueIsNull() { 475 476 assertEquals("c", select.get()); 477 b.setNext(null); 478 assertEquals(Arrays.asList(a.nextProperty()), dependencies); 479 assertNull(select.get()); 480 assertEquals(Arrays.asList(a.nextProperty(), b.nextProperty()), dependencies); 481 log.checkFine(NullPointerException.class); 482 } 483 484 @SuppressWarnings("unchecked") 485 @Test 486 public void testWhenAnIncompleteChainIsMadeCompleteInTheMiddleThatTheValueIsComputedCorrectly() { 487 488 b.setNext(null); 489 select.get(); 490 log.reset(); 491 b.setNext(c); 492 assertEquals(Arrays.asList(a.nextProperty()), dependencies); 493 assertEquals("c", select.get()); 494 assertEquals(Arrays.asList(a.nextProperty(), b.nextProperty(), c.nameProperty()), dependencies); 495 assertTrue(log.isEmpty()); 496 } 497 498 @SuppressWarnings("unchecked") 499 @Test 500 public void testWhenAValidChangeIsBrokenBySettingTheLastLinkToNullThenTheValueIsNull() { 501 log.reset(); 502 assertEquals("c", select.get()); 503 c.setName("d"); 504 assertEquals(Arrays.asList(a.nextProperty()), dependencies); 505 assertEquals("d", select.get()); 506 assertEquals(Arrays.asList(a.nextProperty(), b.nextProperty(), c.nameProperty()), dependencies); 507 assertTrue(log.isEmpty()); 508 } 509 510 @SuppressWarnings("unchecked") 511 @Test 512 public void testWhenAnIncompleteChainIsMadeCompleteAtTheEndThatTheValueIsComputedCorrectly() { 513 log.reset(); 514 c.setName("d"); 515 select.get(); 516 c.setName("c"); 517 assertEquals(Arrays.asList(a.nextProperty()), dependencies); 518 assertEquals("c", select.get()); 519 assertEquals(Arrays.asList(a.nextProperty(), b.nextProperty(), c.nameProperty()), dependencies); 520 assertTrue(log.isEmpty()); 521 } 522 523 @SuppressWarnings("unchecked") 524 @Test 525 public void testSettingTheRootValueToNullInAChainShouldUnhookAllListeners() { 526 select.get(); 527 a.setNext(null); 528 529 // All of the listeners should have been uninstalled 530 assertEquals(0, a.numChangedListenersForName); 531 assertEquals(1, a.numChangedListenersForNext); 532 533 assertEquals(0, b.numChangedListenersForName); 534 assertEquals(0, b.numChangedListenersForNext); 535 536 assertEquals(0, c.numChangedListenersForName); 537 assertEquals(0, c.numChangedListenersForNext); 538 539 assertEquals(0, d.numChangedListenersForName); 540 assertEquals(0, d.numChangedListenersForNext); 541 542 assertEquals(Arrays.asList(a.nextProperty()), dependencies); 543 } 544 545 /** 546 * This test performs 10,000 random operations on the chain of a.b.c 547 * (setting different values for each step, sometimes doing multiple 548 * assignments between get() calls, random invalidate() calls). After each 549 * random iteration, we check to see if the listeners installed on the a, b, 550 * c objects are still correct. The goal is to catch any freak situations 551 * where we might have installed two listeners on the same property, or 552 * failed to remove a listener for a property under some circumstance. 553 * 554 * Do note that the only listeners that are installed in this method are 555 * done by the select binding. 556 */ 557 @SuppressWarnings("unchecked") 558 @Test 559 public void stressTestRandomOperationsResultInCorrectListenersInstalled() { 560 561 List<String> steps = new ArrayList<String>(); 562 563 Random rand = new Random(System.currentTimeMillis()); 564 for (int i = 0; i < 10000; i++) { 565 switch (rand.nextInt(20)) { 566 case 0: 567 a.setNext(null); 568 steps.add("Assign a.value to null"); 569 break; 570 case 1: 571 a.setNext(b); 572 steps.add("Assign a.value to b"); 573 break; 574 case 2: 575 b.setNext(null); 576 steps.add("Assign b.value to null"); 577 break; 578 case 3: 579 b.setNext(c); 580 steps.add("Assign b.value to c"); 581 break; 582 case 4: 583 c.setNext(null); 584 steps.add("Assign c.value to null"); 585 break; 586 case 5: 587 c.setNext(d); 588 steps.add("Assign c.value to d"); 589 break; 590 case 6: 591 c.setName(null); 592 steps.add("Assign c.name to null"); 593 break; 594 case 7: 595 c.setName("c"); 596 steps.add("Assign c.name to 'c'"); 597 break; 598 default: 599 select.get(); 600 steps.add("Call select.get()"); 601 } 602 603 // Now validate that the listeners are as we expected 604 int expected = 1; 605 int depsCount = expected; 606 assertEquals(0, a.numChangedListenersForName); 607 if (expected != a.numChangedListenersForNext) { 608 printSteps(i, steps); 609 } 610 assertEquals(expected, a.numChangedListenersForNext); 611 612 expected = select.isValid() && a.getNext() == b ? 1 : 0; 613 depsCount += expected; 614 assertEquals(0, b.numChangedListenersForName); 615 if (expected != b.numChangedListenersForNext) { 616 printSteps(i, steps); 617 } 618 assertEquals(expected, b.numChangedListenersForNext); 619 620 expected = select.isValid() && a.getNext() == b && b.getNext() == c ? 1 : 0; 621 depsCount += expected; 622 assertEquals(0, c.numChangedListenersForNext); 623 if (expected != c.numChangedListenersForName) { 624 printSteps(i, steps); 625 } 626 assertEquals(expected, c.numChangedListenersForName); 627 628 assertEquals(0, d.numChangedListenersForName); 629 assertEquals(0, d.numChangedListenersForNext); 630 631 switch (depsCount) { 632 case 0: 633 case 1: 634 assertEquals(Arrays.asList(a.nextProperty()), dependencies); 635 break; 636 case 2: 637 assertEquals(Arrays.asList(a.nextProperty(), b.nextProperty()), dependencies); 638 break; 639 case 3: 640 assertEquals(Arrays.asList(a.nextProperty(), b.nextProperty(), c.nameProperty()), dependencies); 641 break; 642 default: 643 fail("Should not reach here"); 644 } 645 } 646 } 647 648 private void printSteps(int iteration, List<String> steps) { 649 System.err.println("Failed on iteration " + iteration + " for the following observableArrayList of changes"); 650 for (String s : steps) { 651 System.err.println("\t" + s); 652 } 653 } 654 }