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