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 }