1 /*
   2  * Copyright (c) 2012, 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.InvalidationListenerMock;
  30 import javafx.beans.Observable;
  31 import javafx.beans.WeakInvalidationListenerMock;
  32 import javafx.beans.value.ChangeListener;
  33 import javafx.beans.value.ChangeListenerMock;
  34 import javafx.beans.value.ObservableValue;
  35 import javafx.beans.value.ObservableValueStub;
  36 import javafx.beans.value.WeakChangeListenerMock;
  37 import org.junit.Before;
  38 import org.junit.Test;
  39 
  40 import java.util.BitSet;
  41 import java.util.concurrent.atomic.AtomicBoolean;
  42 import java.util.concurrent.atomic.AtomicInteger;
  43 
  44 import static org.junit.Assert.assertEquals;
  45 import static org.junit.Assert.assertTrue;
  46 
  47 public class ExpressionHelperTest {
  48 
  49     private static final Object UNDEFINED = new Object();
  50     private static final Object DATA_1 = new Object();
  51     private static final Object DATA_2 = new Object();
  52 
  53     private ExpressionHelper helper;
  54     private ObservableValueStub observable;
  55     private InvalidationListenerMock[] invalidationListener;
  56     private ChangeListenerMock<Object>[] changeListener;
  57 
  58     @Before
  59     public void setUp() {
  60         helper = null;
  61         observable = new ObservableValueStub(DATA_1);
  62         invalidationListener = new InvalidationListenerMock[] {
  63                 new InvalidationListenerMock(), new InvalidationListenerMock(), new InvalidationListenerMock(), new InvalidationListenerMock()
  64         };
  65         changeListener = new ChangeListenerMock[] {
  66                 new ChangeListenerMock<Object>(UNDEFINED), new ChangeListenerMock<Object>(UNDEFINED), new ChangeListenerMock<Object>(UNDEFINED), new ChangeListenerMock<Object>(UNDEFINED)
  67         };
  68     }
  69 
  70     @Test (expected = NullPointerException.class)
  71     public void testAddInvalidation_Null_X() {
  72         ExpressionHelper.addListener(helper, null, invalidationListener[0]);
  73     }
  74 
  75     @Test (expected = NullPointerException.class)
  76     public void testAddInvalidation_X_Null() {
  77         ExpressionHelper.addListener(helper, observable, (InvalidationListener) null);
  78     }
  79 
  80     @Test (expected = NullPointerException.class)
  81     public void testRemoveInvalidation_Null() {
  82         ExpressionHelper.removeListener(helper, (InvalidationListener) null);
  83     }
  84 
  85     @Test (expected = NullPointerException.class)
  86     public void testAddChange_Null_X() {
  87         ExpressionHelper.addListener(helper, null, changeListener[0]);
  88     }
  89 
  90     @Test (expected = NullPointerException.class)
  91     public void testAddChange_X_Null() {
  92         ExpressionHelper.addListener(helper, observable, (ChangeListener) null);
  93     }
  94 
  95     @Test (expected = NullPointerException.class)
  96     public void testRemoveChange_Null() {
  97         ExpressionHelper.removeListener(helper, (ChangeListener) null);
  98     }
  99 
 100     @Test
 101     public void testEmptyHelper() {
 102         // all of these calls should be no-ops
 103         ExpressionHelper.removeListener(helper, invalidationListener[0]);
 104         ExpressionHelper.removeListener(helper, changeListener[0]);
 105         ExpressionHelper.fireValueChangedEvent(helper);
 106     }
 107 
 108     @Test
 109     public void testSingeInvalidation() {
 110         helper = ExpressionHelper.addListener(helper, observable, invalidationListener[0]);
 111         ExpressionHelper.fireValueChangedEvent(helper);
 112         invalidationListener[0].check(observable, 1);
 113 
 114         helper = ExpressionHelper.removeListener(helper, invalidationListener[1]);
 115         ExpressionHelper.fireValueChangedEvent(helper);
 116         invalidationListener[0].check(observable, 1);
 117         invalidationListener[1].check(null, 0);
 118 
 119         helper = ExpressionHelper.removeListener(helper, changeListener[1]);
 120         ExpressionHelper.fireValueChangedEvent(helper);
 121         invalidationListener[0].check(observable, 1);
 122         changeListener[1].check(null, UNDEFINED, UNDEFINED, 0);
 123 
 124         helper = ExpressionHelper.addListener(helper, observable, invalidationListener[1]);
 125         ExpressionHelper.fireValueChangedEvent(helper);
 126         invalidationListener[0].check(observable, 1);
 127         invalidationListener[1].check(observable, 1);
 128 
 129         helper = ExpressionHelper.removeListener(helper, invalidationListener[1]);
 130         ExpressionHelper.fireValueChangedEvent(helper);
 131         invalidationListener[0].check(observable, 1);
 132         invalidationListener[1].check(null, 0);
 133 
 134         helper = ExpressionHelper.addListener(helper, observable, changeListener[1]);
 135         observable.set(DATA_2);
 136         ExpressionHelper.fireValueChangedEvent(helper);
 137         invalidationListener[0].check(observable, 1);
 138         changeListener[1].check(observable, DATA_1, DATA_2, 1);
 139 
 140         helper = ExpressionHelper.removeListener(helper, changeListener[1]);
 141         observable.set(DATA_1);
 142         ExpressionHelper.fireValueChangedEvent(helper);
 143         invalidationListener[0].check(observable, 1);
 144         changeListener[1].check(null, UNDEFINED, UNDEFINED, 0);
 145 
 146         helper = ExpressionHelper.removeListener(helper, invalidationListener[0]);
 147         ExpressionHelper.fireValueChangedEvent(helper);
 148         invalidationListener[0].check(null, 0);
 149     }
 150 
 151     @Test
 152     public void testSingeChange() {
 153         helper = ExpressionHelper.addListener(helper, observable, changeListener[0]);
 154         observable.set(DATA_2);
 155         ExpressionHelper.fireValueChangedEvent(helper);
 156         changeListener[0].check(observable, DATA_1, DATA_2, 1);
 157 
 158         helper = ExpressionHelper.removeListener(helper, invalidationListener[1]);
 159         observable.set(DATA_1);
 160         ExpressionHelper.fireValueChangedEvent(helper);
 161         changeListener[0].check(observable, DATA_2, DATA_1, 1);
 162         invalidationListener[1].check(null, 0);
 163 
 164         helper = ExpressionHelper.removeListener(helper, changeListener[1]);
 165         observable.set(DATA_2);
 166         ExpressionHelper.fireValueChangedEvent(helper);
 167         changeListener[0].check(observable, DATA_1, DATA_2, 1);
 168         changeListener[1].check(null, UNDEFINED, UNDEFINED, 0);
 169 
 170         helper = ExpressionHelper.addListener(helper, observable, invalidationListener[1]);
 171         observable.set(DATA_1);
 172         ExpressionHelper.fireValueChangedEvent(helper);
 173         changeListener[0].check(observable, DATA_2, DATA_1, 1);
 174         invalidationListener[1].check(observable, 1);
 175 
 176         helper = ExpressionHelper.removeListener(helper, invalidationListener[1]);
 177         observable.set(DATA_2);
 178         ExpressionHelper.fireValueChangedEvent(helper);
 179         changeListener[0].check(observable, DATA_1, DATA_2, 1);
 180         invalidationListener[1].check(null, 0);
 181 
 182         helper = ExpressionHelper.addListener(helper, observable, changeListener[1]);
 183         observable.set(DATA_1);
 184         ExpressionHelper.fireValueChangedEvent(helper);
 185         changeListener[0].check(observable, DATA_2, DATA_1, 1);
 186         changeListener[1].check(observable, DATA_2, DATA_1, 1);
 187 
 188         helper = ExpressionHelper.removeListener(helper, changeListener[1]);
 189         observable.set(DATA_2);
 190         ExpressionHelper.fireValueChangedEvent(helper);
 191         changeListener[0].check(observable, DATA_1, DATA_2, 1);
 192         changeListener[1].check(null, UNDEFINED, UNDEFINED, 0);
 193 
 194         helper = ExpressionHelper.removeListener(helper, changeListener[0]);
 195         ExpressionHelper.fireValueChangedEvent(helper);
 196         changeListener[0].check(null, UNDEFINED, UNDEFINED, 0);
 197     }
 198 
 199     @Test
 200     public void testAddInvalidation() {
 201         final InvalidationListener weakListener = new WeakInvalidationListenerMock();
 202 
 203         helper = ExpressionHelper.addListener(helper, observable, changeListener[0]);
 204         helper = ExpressionHelper.addListener(helper, observable, changeListener[1]);
 205 
 206         helper = ExpressionHelper.addListener(helper, observable, weakListener);
 207         helper = ExpressionHelper.addListener(helper, observable, invalidationListener[0]);
 208         ExpressionHelper.fireValueChangedEvent(helper);
 209         invalidationListener[0].check(observable, 1);
 210 
 211         helper = ExpressionHelper.addListener(helper, observable, weakListener);
 212         helper = ExpressionHelper.addListener(helper, observable, invalidationListener[1]);
 213         ExpressionHelper.fireValueChangedEvent(helper);
 214         invalidationListener[0].check(observable, 1);
 215         invalidationListener[1].check(observable, 1);
 216 
 217         helper = ExpressionHelper.addListener(helper, observable, weakListener);
 218         helper = ExpressionHelper.addListener(helper, observable, invalidationListener[2]);
 219         ExpressionHelper.fireValueChangedEvent(helper);
 220         invalidationListener[0].check(observable, 1);
 221         invalidationListener[1].check(observable, 1);
 222         invalidationListener[2].check(observable, 1);
 223     }
 224 
 225     @Test
 226     public void testRemoveInvalidation() {
 227         helper = ExpressionHelper.addListener(helper, observable, changeListener[0]);
 228         helper = ExpressionHelper.addListener(helper, observable, changeListener[1]);
 229 
 230         helper = ExpressionHelper.removeListener(helper, invalidationListener[1]);
 231 
 232         helper = ExpressionHelper.addListener(helper, observable, invalidationListener[0]);
 233 
 234         helper = ExpressionHelper.removeListener(helper, invalidationListener[1]);
 235 
 236         helper = ExpressionHelper.addListener(helper, observable, invalidationListener[2]);
 237         helper = ExpressionHelper.addListener(helper, observable, invalidationListener[1]);
 238 
 239         helper = ExpressionHelper.removeListener(helper, invalidationListener[0]);
 240         ExpressionHelper.fireValueChangedEvent(helper);
 241         invalidationListener[0].check(null, 0);
 242         invalidationListener[1].check(observable, 1);
 243         invalidationListener[2].check(observable, 1);
 244 
 245         helper = ExpressionHelper.removeListener(helper, invalidationListener[1]);
 246         ExpressionHelper.fireValueChangedEvent(helper);
 247         invalidationListener[0].check(null, 0);
 248         invalidationListener[1].check(null, 0);
 249         invalidationListener[2].check(observable, 1);
 250 
 251         helper = ExpressionHelper.removeListener(helper, invalidationListener[2]);
 252         ExpressionHelper.fireValueChangedEvent(helper);
 253         invalidationListener[0].check(null, 0);
 254         invalidationListener[1].check(null, 0);
 255         invalidationListener[2].check(null, 0);
 256     }
 257 
 258     @Test
 259     public void testAddInvalidationWhileLocked() {
 260         final ChangeListener<Object> addingListener = new ChangeListener() {
 261             int index = 0;
 262             @Override public void changed(ObservableValue observable, Object oldValue, Object newValue) {
 263                 if (index < invalidationListener.length) {
 264                     helper = ExpressionHelper.addListener(helper, ExpressionHelperTest.this.observable, invalidationListener[index++]);
 265                 }
 266             }
 267         };
 268         helper = ExpressionHelper.addListener(helper, observable, addingListener);
 269         helper = ExpressionHelper.addListener(helper, observable, changeListener[0]);
 270 
 271         observable.set(DATA_2);
 272         ExpressionHelper.fireValueChangedEvent(helper);
 273 
 274         invalidationListener[0].reset();
 275         observable.set(DATA_1);
 276         ExpressionHelper.fireValueChangedEvent(helper);
 277         invalidationListener[0].check(observable, 1);
 278 
 279         invalidationListener[1].reset();
 280         observable.set(DATA_2);
 281         ExpressionHelper.fireValueChangedEvent(helper);
 282         invalidationListener[0].check(observable, 1);
 283         invalidationListener[1].check(observable, 1);
 284 
 285         invalidationListener[2].reset();
 286         observable.set(DATA_1);
 287         ExpressionHelper.fireValueChangedEvent(helper);
 288         invalidationListener[0].check(observable, 1);
 289         invalidationListener[1].check(observable, 1);
 290         invalidationListener[2].check(observable, 1);
 291 
 292         invalidationListener[3].reset();
 293         observable.set(DATA_2);
 294         ExpressionHelper.fireValueChangedEvent(helper);
 295         invalidationListener[0].check(observable, 1);
 296         invalidationListener[1].check(observable, 1);
 297         invalidationListener[2].check(observable, 1);
 298         invalidationListener[3].check(observable, 1);
 299     }
 300 
 301     @Test
 302     public void testRemoveInvalidationWhileLocked() {
 303         final ChangeListener<Object> removingListener = new ChangeListener() {
 304             int index = 0;
 305             @Override public void changed(ObservableValue observable, Object oldValue, Object newValue) {
 306                 if (index < invalidationListener.length) {
 307                     helper = ExpressionHelper.removeListener(helper, invalidationListener[index++]);
 308                 }
 309             }
 310         };
 311         helper = ExpressionHelper.addListener(helper, observable, removingListener);
 312         helper = ExpressionHelper.addListener(helper, observable, changeListener[0]);
 313         helper = ExpressionHelper.addListener(helper, observable, invalidationListener[0]);
 314         helper = ExpressionHelper.addListener(helper, observable, invalidationListener[2]);
 315         helper = ExpressionHelper.addListener(helper, observable, invalidationListener[1]);
 316 
 317         observable.set(DATA_2);
 318         ExpressionHelper.fireValueChangedEvent(helper);
 319         invalidationListener[0].reset();
 320         invalidationListener[1].check(observable, 1);
 321         invalidationListener[2].check(observable, 1);
 322 
 323         observable.set(DATA_1);
 324         ExpressionHelper.fireValueChangedEvent(helper);
 325         invalidationListener[0].check(null, 0);
 326         invalidationListener[1].reset();
 327         invalidationListener[2].check(observable, 1);
 328 
 329         observable.set(DATA_2);
 330         ExpressionHelper.fireValueChangedEvent(helper);
 331         invalidationListener[0].check(null, 0);
 332         invalidationListener[1].check(null, 0);
 333         invalidationListener[2].reset();
 334 
 335         observable.set(DATA_1);
 336         ExpressionHelper.fireValueChangedEvent(helper);
 337         invalidationListener[0].check(null, 0);
 338         invalidationListener[1].check(null, 0);
 339         invalidationListener[2].check(null, 0);
 340     }
 341 
 342     @Test
 343     public void testAddChange() {
 344         final ChangeListener<Object> weakListener = new WeakChangeListenerMock();
 345 
 346         helper = ExpressionHelper.addListener(helper, observable, invalidationListener[0]);
 347         helper = ExpressionHelper.addListener(helper, observable, invalidationListener[1]);
 348 
 349         helper = ExpressionHelper.addListener(helper, observable, weakListener);
 350         helper = ExpressionHelper.addListener(helper, observable, changeListener[0]);
 351         observable.set(DATA_2);
 352         ExpressionHelper.fireValueChangedEvent(helper);
 353         changeListener[0].check(observable, DATA_1, DATA_2, 1);
 354 
 355         helper = ExpressionHelper.addListener(helper, observable, weakListener);
 356         helper = ExpressionHelper.addListener(helper, observable, changeListener[1]);
 357         observable.set(DATA_1);
 358         ExpressionHelper.fireValueChangedEvent(helper);
 359         changeListener[0].check(observable, DATA_2, DATA_1, 1);
 360         changeListener[1].check(observable, DATA_2, DATA_1, 1);
 361 
 362         helper = ExpressionHelper.addListener(helper, observable, weakListener);
 363         helper = ExpressionHelper.addListener(helper, observable, changeListener[2]);
 364         observable.set(DATA_2);
 365         ExpressionHelper.fireValueChangedEvent(helper);
 366         changeListener[0].check(observable, DATA_1, DATA_2, 1);
 367         changeListener[1].check(observable, DATA_1, DATA_2, 1);
 368         changeListener[2].check(observable, DATA_1, DATA_2, 1);
 369     }
 370 
 371     @Test
 372     public void testRemoveChange() {
 373         helper = ExpressionHelper.addListener(helper, observable, invalidationListener[0]);
 374         helper = ExpressionHelper.addListener(helper, observable, invalidationListener[1]);
 375 
 376         helper = ExpressionHelper.removeListener(helper, changeListener[1]);
 377 
 378         helper = ExpressionHelper.addListener(helper, observable, changeListener[0]);
 379 
 380         helper = ExpressionHelper.removeListener(helper, changeListener[1]);
 381 
 382         helper = ExpressionHelper.addListener(helper, observable, changeListener[2]);
 383         helper = ExpressionHelper.addListener(helper, observable, changeListener[1]);
 384 
 385         helper = ExpressionHelper.removeListener(helper, changeListener[0]);
 386         observable.set(DATA_2);
 387         ExpressionHelper.fireValueChangedEvent(helper);
 388         changeListener[0].check(null, UNDEFINED, UNDEFINED, 0);
 389         changeListener[1].check(observable, DATA_1, DATA_2, 1);
 390         changeListener[2].check(observable, DATA_1, DATA_2, 1);
 391 
 392         helper = ExpressionHelper.removeListener(helper, changeListener[1]);
 393         observable.set(DATA_1);
 394         ExpressionHelper.fireValueChangedEvent(helper);
 395         changeListener[0].check(null, UNDEFINED, UNDEFINED, 0);
 396         changeListener[1].check(null, UNDEFINED, UNDEFINED, 0);
 397         changeListener[2].check(observable, DATA_2, DATA_1, 1);
 398 
 399         helper = ExpressionHelper.removeListener(helper, changeListener[2]);
 400         observable.set(DATA_2);
 401         ExpressionHelper.fireValueChangedEvent(helper);
 402         changeListener[0].check(null, UNDEFINED, UNDEFINED, 0);
 403         changeListener[1].check(null, UNDEFINED, UNDEFINED, 0);
 404         changeListener[2].check(null, UNDEFINED, UNDEFINED, 0);
 405     }
 406 
 407     @Test
 408     public void testAddChangeWhileLocked() {
 409         final InvalidationListener addingListener = new InvalidationListener() {
 410             int index = 0;
 411             @Override public void invalidated(Observable observable) {
 412                 if (index < invalidationListener.length) {
 413                     helper = ExpressionHelper.addListener(helper, ExpressionHelperTest.this.observable, changeListener[index++]);
 414                 }
 415             }
 416         };
 417         helper = ExpressionHelper.addListener(helper, observable, addingListener);
 418         helper = ExpressionHelper.addListener(helper, observable, invalidationListener[0]);
 419 
 420         observable.set(DATA_2);
 421         ExpressionHelper.fireValueChangedEvent(helper);
 422 
 423         changeListener[0].reset();
 424         observable.set(DATA_1);
 425         ExpressionHelper.fireValueChangedEvent(helper);
 426         changeListener[0].check(observable, DATA_2, DATA_1, 1);
 427 
 428         changeListener[1].reset();
 429         observable.set(DATA_2);
 430         ExpressionHelper.fireValueChangedEvent(helper);
 431         changeListener[0].check(observable, DATA_1, DATA_2, 1);
 432         changeListener[1].check(observable, DATA_1, DATA_2, 1);
 433 
 434         changeListener[2].reset();
 435         observable.set(DATA_1);
 436         ExpressionHelper.fireValueChangedEvent(helper);
 437         changeListener[0].check(observable, DATA_2, DATA_1, 1);
 438         changeListener[1].check(observable, DATA_2, DATA_1, 1);
 439         changeListener[2].check(observable, DATA_2, DATA_1, 1);
 440 
 441         changeListener[3].reset();
 442         observable.set(DATA_2);
 443         ExpressionHelper.fireValueChangedEvent(helper);
 444         changeListener[0].check(observable, DATA_1, DATA_2, 1);
 445         changeListener[1].check(observable, DATA_1, DATA_2, 1);
 446         changeListener[2].check(observable, DATA_1, DATA_2, 1);
 447         changeListener[3].check(observable, DATA_1, DATA_2, 1);
 448     }
 449 
 450     @Test
 451     public void testRemoveChangeWhileLocked() {
 452         final InvalidationListener removingListener = new InvalidationListener() {
 453             int index = 0;
 454             @Override public void invalidated(Observable observable) {
 455                 if (index < invalidationListener.length) {
 456                     helper = ExpressionHelper.removeListener(helper, changeListener[index++]);
 457                 }
 458             }
 459         };
 460         helper = ExpressionHelper.addListener(helper, observable, removingListener);
 461         helper = ExpressionHelper.addListener(helper, observable, invalidationListener[0]);
 462         helper = ExpressionHelper.addListener(helper, observable, changeListener[0]);
 463         helper = ExpressionHelper.addListener(helper, observable, changeListener[2]);
 464         helper = ExpressionHelper.addListener(helper, observable, changeListener[1]);
 465 
 466         observable.set(DATA_2);
 467         ExpressionHelper.fireValueChangedEvent(helper);
 468         changeListener[0].reset();
 469         changeListener[1].check(observable, DATA_1, DATA_2, 1);
 470         changeListener[2].check(observable, DATA_1, DATA_2, 1);
 471 
 472         observable.set(DATA_1);
 473         ExpressionHelper.fireValueChangedEvent(helper);
 474         changeListener[0].check(null, UNDEFINED, UNDEFINED, 0);
 475         changeListener[1].reset();
 476         changeListener[2].check(observable, DATA_2, DATA_1, 1);
 477 
 478         observable.set(DATA_2);
 479         ExpressionHelper.fireValueChangedEvent(helper);
 480         changeListener[0].check(null, UNDEFINED, UNDEFINED, 0);
 481         changeListener[1].check(null, UNDEFINED, UNDEFINED, 0);
 482         changeListener[2].reset();
 483 
 484         observable.set(DATA_1);
 485         ExpressionHelper.fireValueChangedEvent(helper);
 486         changeListener[0].check(null, UNDEFINED, UNDEFINED, 0);
 487         changeListener[1].check(null, UNDEFINED, UNDEFINED, 0);
 488         changeListener[2].check(null, UNDEFINED, UNDEFINED, 0);
 489     }
 490 
 491     @Test
 492     public void testFireValueChangedEvent() {
 493         helper = ExpressionHelper.addListener(helper, observable, invalidationListener[0]);
 494         helper = ExpressionHelper.addListener(helper, observable, changeListener[0]);
 495 
 496         ExpressionHelper.fireValueChangedEvent(helper);
 497         invalidationListener[0].check(observable, 1);
 498         changeListener[0].check(null, UNDEFINED, UNDEFINED, 0);
 499 
 500         observable.set(null);
 501         ExpressionHelper.fireValueChangedEvent(helper);
 502         invalidationListener[0].check(observable, 1);
 503         changeListener[0].check(observable, DATA_1, null, 1);
 504 
 505         ExpressionHelper.fireValueChangedEvent(helper);
 506         invalidationListener[0].check(observable, 1);
 507         changeListener[0].check(null, UNDEFINED, UNDEFINED, 0);
 508 
 509         observable.set(DATA_1);
 510         ExpressionHelper.fireValueChangedEvent(helper);
 511         invalidationListener[0].check(observable, 1);
 512         changeListener[0].check(observable, null, DATA_1, 1);
 513 
 514         ExpressionHelper.fireValueChangedEvent(helper);
 515         invalidationListener[0].check(observable, 1);
 516         changeListener[0].check(null, UNDEFINED, UNDEFINED, 0);
 517     }
 518 
 519     @Test
 520     public void testExceptionNotPropagatedFromSingleInvalidation() {
 521         helper = ExpressionHelper.addListener(helper, observable,(o) -> {throw new RuntimeException();});
 522         observable.set(null);
 523         helper.fireValueChangedEvent();
 524     }
 525 
 526     @Test
 527     public void testExceptionNotPropagatedFromMultipleInvalidation() {
 528         BitSet called = new BitSet();
 529 
 530         helper = ExpressionHelper.addListener(helper, observable, (o) -> {called.set(0); throw new RuntimeException();});
 531         helper = ExpressionHelper.addListener(helper, observable, (o) -> {called.set(1); throw new RuntimeException();});
 532         observable.set(null);
 533         helper.fireValueChangedEvent();
 534 
 535         assertTrue(called.get(0));
 536         assertTrue(called.get(1));
 537     }
 538 
 539     @Test
 540     public void testExceptionNotPropagatedFromSingleChange() {
 541         helper = ExpressionHelper.addListener(helper, observable, (value, o1, o2) -> {throw new RuntimeException();});
 542         observable.set(null);
 543         helper.fireValueChangedEvent();
 544     }
 545 
 546     @Test
 547     public void testExceptionNotPropagatedFromMultipleChange() {
 548         BitSet called = new BitSet();
 549 
 550         helper = ExpressionHelper.addListener(helper, observable, (value, o1, o2) -> {called.set(0); throw new RuntimeException();});
 551         helper = ExpressionHelper.addListener(helper, observable, (value, o1, o2) -> {called.set(1); throw new RuntimeException();});
 552         observable.set(null);
 553         helper.fireValueChangedEvent();
 554 
 555         assertTrue(called.get(0));
 556         assertTrue(called.get(1));
 557     }
 558 
 559     @Test
 560     public void testExceptionNotPropagatedFromMultipleChangeAndInvalidation() {
 561         BitSet called = new BitSet();
 562 
 563         helper = ExpressionHelper.addListener(helper, observable, (value, o1, o2) -> {called.set(0); throw new RuntimeException();});
 564         helper = ExpressionHelper.addListener(helper, observable, (value, o1, o2) -> {called.set(1); throw new RuntimeException();});
 565         helper = ExpressionHelper.addListener(helper, observable, (o) -> {called.set(2); throw new RuntimeException();});
 566         helper = ExpressionHelper.addListener(helper, observable, (o) -> {called.set(3); throw new RuntimeException();});
 567         observable.set(null);
 568         helper.fireValueChangedEvent();
 569 
 570         assertTrue(called.get(0));
 571         assertTrue(called.get(1));
 572         assertTrue(called.get(2));
 573         assertTrue(called.get(3));
 574     }
 575 
 576     @Test
 577     public void testExceptionHandledByThreadUncaughtHandlerInSingleInvalidation() {
 578         AtomicBoolean called = new AtomicBoolean(false);
 579 
 580         Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.set(true));
 581 
 582         helper = ExpressionHelper.addListener(helper, observable,(o) -> {throw new RuntimeException();});
 583         observable.set(null);
 584         helper.fireValueChangedEvent();
 585 
 586         assertTrue(called.get());
 587     }
 588 
 589 
 590     @Test
 591     public void testExceptionHandledByThreadUncaughtHandlerInMultipleInvalidation() {
 592         AtomicInteger called = new AtomicInteger(0);
 593 
 594         Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.incrementAndGet());
 595 
 596         helper = ExpressionHelper.addListener(helper, observable, (o) -> {throw new RuntimeException();});
 597         helper = ExpressionHelper.addListener(helper, observable, (o) -> {throw new RuntimeException();});
 598         observable.set(null);
 599         helper.fireValueChangedEvent();
 600 
 601         assertEquals(2, called.get());
 602     }
 603 
 604     @Test
 605     public void testExceptionHandledByThreadUncaughtHandlerInSingleChange() {
 606         AtomicBoolean called = new AtomicBoolean(false);
 607 
 608         Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.set(true));
 609         helper = ExpressionHelper.addListener(helper, observable, (value, o1, o2) -> {throw new RuntimeException();});
 610         observable.set(null);
 611         helper.fireValueChangedEvent();
 612 
 613         assertTrue(called.get());
 614     }
 615 
 616     @Test
 617     public void testExceptionHandledByThreadUncaughtHandlerInMultipleChange() {
 618         AtomicInteger called = new AtomicInteger(0);
 619 
 620         Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.incrementAndGet());
 621 
 622         helper = ExpressionHelper.addListener(helper, observable, (value, o1, o2) -> {throw new RuntimeException();});
 623         helper = ExpressionHelper.addListener(helper, observable, (value, o1, o2) -> {throw new RuntimeException();});
 624         observable.set(null);
 625         helper.fireValueChangedEvent();
 626 
 627         assertEquals(2, called.get());
 628     }
 629 
 630     @Test
 631     public void testExceptionHandledByThreadUncaughtHandlerInMultipleChangeAndInvalidation() {
 632         AtomicInteger called = new AtomicInteger(0);
 633 
 634         Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.incrementAndGet());
 635 
 636         helper = ExpressionHelper.addListener(helper, observable, (value, o1, o2) -> { throw new RuntimeException();});
 637         helper = ExpressionHelper.addListener(helper, observable, (value, o1, o2) -> { throw new RuntimeException();});
 638         helper = ExpressionHelper.addListener(helper, observable, (o) -> { throw new RuntimeException();});
 639         helper = ExpressionHelper.addListener(helper, observable, (o) -> {throw new RuntimeException();});
 640         observable.set(null);
 641         helper.fireValueChangedEvent();
 642 
 643         assertEquals(4, called.get());
 644     }
 645 
 646 }