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 test.com.sun.javafx.collections;
  27 
  28 import com.sun.javafx.collections.ListListenerHelper;
  29 import com.sun.javafx.collections.NonIterableChange;
  30 import javafx.beans.InvalidationListener;
  31 import test.javafx.beans.InvalidationListenerMock;
  32 import javafx.beans.Observable;
  33 import javafx.collections.FXCollections;
  34 import javafx.collections.ListChangeListener;
  35 import test.javafx.collections.MockListObserver;
  36 import javafx.collections.ObservableList;
  37 
  38 import org.junit.Before;
  39 import org.junit.Test;
  40 
  41 import java.util.BitSet;
  42 import java.util.concurrent.atomic.AtomicBoolean;
  43 import java.util.concurrent.atomic.AtomicInteger;
  44 
  45 import static org.junit.Assert.assertEquals;
  46 import static org.junit.Assert.assertFalse;
  47 import static org.junit.Assert.assertTrue;
  48 
  49 public class ListListenerHelperTest {
  50     
  51     private InvalidationListenerMock[] invalidationListenerMock;
  52 
  53     private MockListObserver<Object>[] changeListenerMock;
  54 
  55     private ListListenerHelper<Object> helper;
  56 
  57     private ObservableList<Object> list;
  58     private ListChangeListener.Change<Object> change;
  59 
  60     @Before
  61     public void setUp() {
  62         invalidationListenerMock = new InvalidationListenerMock[] {
  63                 new InvalidationListenerMock(),
  64                 new InvalidationListenerMock(),
  65                 new InvalidationListenerMock(),
  66                 new InvalidationListenerMock()
  67         };
  68         changeListenerMock = new MockListObserver[] {
  69                 new MockListObserver<Object>(),
  70                 new MockListObserver<Object>(),
  71                 new MockListObserver<Object>(),
  72                 new MockListObserver<Object>()
  73         };
  74         helper = null;
  75         list = FXCollections.emptyObservableList();
  76         change = new NonIterableChange.SimpleRemovedChange<Object>(0, 1, new Object(), list);
  77     }
  78 
  79     private void resetAllListeners() {
  80         for (final InvalidationListenerMock listener : invalidationListenerMock) {
  81             listener.reset();
  82         }
  83         for (final MockListObserver<Object> listener : changeListenerMock) {
  84             listener.clear();
  85         }
  86     }
  87 
  88     @Test(expected = NullPointerException.class)
  89     public void testAddInvalidationListener_Null() {
  90         ListListenerHelper.addListener(helper, (InvalidationListener)null);
  91     }
  92 
  93     @Test(expected = NullPointerException.class)
  94     public void testRemoveInvalidationListener_Null() {
  95         ListListenerHelper.removeListener(helper, (InvalidationListener) null);
  96     }
  97 
  98     @Test(expected = NullPointerException.class)
  99     public void testRemoveListChangeListener_Null() {
 100         ListListenerHelper.removeListener(helper, (ListChangeListener<Object>) null);
 101     }
 102 
 103     @Test(expected = NullPointerException.class)
 104     public void testAddListChangeListener_Null() {
 105         ListListenerHelper.addListener(helper, (ListChangeListener<Object>) null);
 106     }
 107 
 108     @Test
 109     public void testEmpty() {
 110         assertFalse(ListListenerHelper.hasListeners(helper));
 111 
 112         // these should be no-ops
 113         ListListenerHelper.fireValueChangedEvent(helper, change);
 114         ListListenerHelper.removeListener(helper, invalidationListenerMock[0]);
 115         ListListenerHelper.removeListener(helper, changeListenerMock[0]);
 116     }
 117 
 118     @Test
 119     public void testInvalidation_Simple() {
 120         helper = ListListenerHelper.addListener(helper, invalidationListenerMock[0]);
 121         ListListenerHelper.fireValueChangedEvent(helper, change);
 122         invalidationListenerMock[0].check(list, 1);
 123 
 124         helper = ListListenerHelper.removeListener(helper, invalidationListenerMock[1]);
 125         ListListenerHelper.fireValueChangedEvent(helper, change);
 126         invalidationListenerMock[0].check(list, 1);
 127         invalidationListenerMock[1].check(null, 0);
 128 
 129         helper = ListListenerHelper.removeListener(helper, changeListenerMock[0]);
 130         ListListenerHelper.fireValueChangedEvent(helper, change);
 131         invalidationListenerMock[0].check(list, 1);
 132         changeListenerMock[0].check0();
 133 
 134         helper = ListListenerHelper.removeListener(helper, invalidationListenerMock[0]);
 135         ListListenerHelper.fireValueChangedEvent(helper, change);
 136         invalidationListenerMock[0].check(null, 0);
 137     }
 138 
 139     @Test
 140     public void testInvalidation_AddInvalidation() {
 141         helper = ListListenerHelper.addListener(helper, invalidationListenerMock[0]);
 142         helper = ListListenerHelper.addListener(helper, invalidationListenerMock[1]);
 143         ListListenerHelper.fireValueChangedEvent(helper, change);
 144         invalidationListenerMock[0].check(list, 1);
 145         invalidationListenerMock[1].check(list, 1);
 146     }
 147 
 148     @Test
 149     public void testInvalidation_AddChange() {
 150         helper = ListListenerHelper.addListener(helper, invalidationListenerMock[0]);
 151         helper = ListListenerHelper.addListener(helper, changeListenerMock[0]);
 152         ListListenerHelper.fireValueChangedEvent(helper, change);
 153         invalidationListenerMock[0].check(list, 1);
 154         changeListenerMock[0].check1();
 155     }
 156     
 157     @Test
 158     public void testInvalidation_ChangeInPulse() {
 159         final InvalidationListener listener = observable -> {
 160             helper = ListListenerHelper.addListener(helper, invalidationListenerMock[0]);
 161         };
 162         helper = ListListenerHelper.addListener(helper, listener);
 163         ListListenerHelper.fireValueChangedEvent(helper, change);
 164         helper = ListListenerHelper.removeListener(helper, listener);
 165         invalidationListenerMock[0].reset();
 166         ListListenerHelper.fireValueChangedEvent(helper, change);
 167         invalidationListenerMock[0].check(list, 1);
 168     }
 169 
 170     @Test
 171     public void testChange_Simple() {
 172         helper = ListListenerHelper.addListener(helper, changeListenerMock[0]);
 173         ListListenerHelper.fireValueChangedEvent(helper, change);
 174         changeListenerMock[0].check1();
 175         changeListenerMock[0].clear();
 176 
 177         helper = ListListenerHelper.removeListener(helper, changeListenerMock[1]);
 178         ListListenerHelper.fireValueChangedEvent(helper, change);
 179         changeListenerMock[0].check1();
 180         changeListenerMock[1].check0();
 181         changeListenerMock[0].clear();
 182 
 183         helper = ListListenerHelper.removeListener(helper, invalidationListenerMock[0]);
 184         ListListenerHelper.fireValueChangedEvent(helper, change);
 185         changeListenerMock[0].check1();
 186         invalidationListenerMock[0].check(null, 0);
 187         changeListenerMock[0].clear();
 188 
 189         helper = ListListenerHelper.removeListener(helper, changeListenerMock[0]);
 190         ListListenerHelper.fireValueChangedEvent(helper, change);
 191         changeListenerMock[0].check0();
 192         changeListenerMock[0].clear();
 193     }
 194 
 195     @Test
 196     public void testChange_AddInvalidation() {
 197         helper = ListListenerHelper.addListener(helper, changeListenerMock[0]);
 198         helper = ListListenerHelper.addListener(helper, invalidationListenerMock[0]);
 199         ListListenerHelper.fireValueChangedEvent(helper, change);
 200         changeListenerMock[0].check1();
 201         invalidationListenerMock[0].check(list, 1);
 202     }
 203 
 204     @Test
 205     public void testChange_AddChange() {
 206         helper = ListListenerHelper.addListener(helper, changeListenerMock[0]);
 207         helper = ListListenerHelper.addListener(helper, changeListenerMock[1]);
 208         ListListenerHelper.fireValueChangedEvent(helper, change);
 209         changeListenerMock[0].check1();
 210         changeListenerMock[1].check1();
 211     }
 212 
 213     @Test
 214     public void testChange_ChangeInPulse() {
 215         final ListChangeListener<Object> listener = c -> {
 216             helper = ListListenerHelper.addListener(helper, changeListenerMock[0]);
 217         };
 218         helper = ListListenerHelper.addListener(helper, listener);
 219         ListListenerHelper.fireValueChangedEvent(helper, change);
 220         helper = ListListenerHelper.removeListener(helper, listener);
 221         changeListenerMock[0].clear();
 222         ListListenerHelper.fireValueChangedEvent(helper, change);
 223         changeListenerMock[0].check1();
 224     }
 225 
 226     @Test
 227     public void testGeneric_AddInvalidation() {
 228         helper = ListListenerHelper.addListener(helper, changeListenerMock[0]);
 229         helper = ListListenerHelper.addListener(helper, changeListenerMock[1]);
 230 
 231         // first invalidation listener creates the array
 232         helper = ListListenerHelper.addListener(helper, invalidationListenerMock[0]);
 233         ListListenerHelper.fireValueChangedEvent(helper, change);
 234         invalidationListenerMock[0].check(list, 1);
 235 
 236         // second and third invalidation listener enlarge the array
 237         helper = ListListenerHelper.addListener(helper, invalidationListenerMock[1]);
 238         helper = ListListenerHelper.addListener(helper, invalidationListenerMock[2]);
 239         ListListenerHelper.fireValueChangedEvent(helper, change);
 240         invalidationListenerMock[0].check(list, 1);
 241         invalidationListenerMock[1].check(list, 1);
 242         invalidationListenerMock[2].check(list, 1);
 243 
 244         // fourth invalidation listener fits into the array
 245         helper = ListListenerHelper.addListener(helper, invalidationListenerMock[3]);
 246         ListListenerHelper.fireValueChangedEvent(helper, change);
 247         invalidationListenerMock[0].check(list, 1);
 248         invalidationListenerMock[1].check(list, 1);
 249         invalidationListenerMock[2].check(list, 1);
 250         invalidationListenerMock[3].check(list, 1);
 251     }
 252     
 253     @Test
 254     public void testGeneric_AddInvalidationInPulse() {
 255         final ListChangeListener<Object> addListener = new ListChangeListener<Object>() {
 256             int counter;
 257             @Override
 258             public void onChanged(Change<? extends Object> c) {
 259                 helper = ListListenerHelper.addListener(helper, invalidationListenerMock[counter++]);
 260             }
 261         };
 262         helper = ListListenerHelper.addListener(helper, changeListenerMock[0]);
 263 
 264         helper = ListListenerHelper.addListener(helper, addListener);
 265         ListListenerHelper.fireValueChangedEvent(helper, change);
 266         helper = ListListenerHelper.removeListener(helper, addListener);
 267         resetAllListeners();
 268         ListListenerHelper.fireValueChangedEvent(helper, change);
 269         invalidationListenerMock[0].check(list, 1);
 270         invalidationListenerMock[1].check(null, 0);
 271         invalidationListenerMock[2].check(null, 0);
 272         invalidationListenerMock[3].check(null, 0);
 273 
 274         helper = ListListenerHelper.addListener(helper, addListener);
 275         ListListenerHelper.fireValueChangedEvent(helper, change);
 276         helper = ListListenerHelper.removeListener(helper, addListener);
 277         resetAllListeners();
 278         ListListenerHelper.fireValueChangedEvent(helper, change);
 279         invalidationListenerMock[0].check(list, 1);
 280         invalidationListenerMock[1].check(list, 1);
 281         invalidationListenerMock[2].check(null, 0);
 282         invalidationListenerMock[3].check(null, 0);
 283 
 284         helper = ListListenerHelper.addListener(helper, addListener);
 285         ListListenerHelper.fireValueChangedEvent(helper, change);
 286         helper = ListListenerHelper.removeListener(helper, addListener);
 287         resetAllListeners();
 288         ListListenerHelper.fireValueChangedEvent(helper, change);
 289         invalidationListenerMock[0].check(list, 1);
 290         invalidationListenerMock[1].check(list, 1);
 291         invalidationListenerMock[2].check(list, 1);
 292         invalidationListenerMock[3].check(null, 0);
 293 
 294         helper = ListListenerHelper.addListener(helper, addListener);
 295         ListListenerHelper.fireValueChangedEvent(helper, change);
 296         helper = ListListenerHelper.removeListener(helper, addListener);
 297         resetAllListeners();
 298         ListListenerHelper.fireValueChangedEvent(helper, change);
 299         invalidationListenerMock[0].check(list, 1);
 300         invalidationListenerMock[1].check(list, 1);
 301         invalidationListenerMock[2].check(list, 1);
 302         invalidationListenerMock[3].check(list, 1);
 303     }
 304 
 305     @Test
 306     public void testGeneric_RemoveInvalidation() {
 307         helper = ListListenerHelper.addListener(helper, invalidationListenerMock[0]);
 308         helper = ListListenerHelper.addListener(helper, invalidationListenerMock[1]);
 309         helper = ListListenerHelper.addListener(helper, invalidationListenerMock[2]);
 310         helper = ListListenerHelper.addListener(helper, invalidationListenerMock[3]);
 311 
 312         // remove first element
 313         helper = ListListenerHelper.removeListener(helper, invalidationListenerMock[0]);
 314         ListListenerHelper.fireValueChangedEvent(helper, change);
 315         invalidationListenerMock[0].check(null, 0);
 316         invalidationListenerMock[1].check(list, 1);
 317         invalidationListenerMock[2].check(list, 1);
 318         invalidationListenerMock[3].check(list, 1);
 319 
 320         // remove middle element
 321         helper = ListListenerHelper.removeListener(helper, invalidationListenerMock[2]);
 322         ListListenerHelper.fireValueChangedEvent(helper, change);
 323         invalidationListenerMock[0].check(null, 0);
 324         invalidationListenerMock[1].check(list, 1);
 325         invalidationListenerMock[2].check(null, 0);
 326         invalidationListenerMock[3].check(list, 1);
 327 
 328         // remove last element
 329         helper = ListListenerHelper.removeListener(helper, invalidationListenerMock[3]);
 330         ListListenerHelper.fireValueChangedEvent(helper, change);
 331         invalidationListenerMock[0].check(null, 0);
 332         invalidationListenerMock[1].check(list, 1);
 333         invalidationListenerMock[2].check(null, 0);
 334         invalidationListenerMock[3].check(null, 0);
 335 
 336         // remove last invalidation with single change
 337         helper = ListListenerHelper.addListener(helper, changeListenerMock[0]);
 338         helper = ListListenerHelper.removeListener(helper, invalidationListenerMock[1]);
 339         ListListenerHelper.fireValueChangedEvent(helper, change);
 340         invalidationListenerMock[1].check(null, 0);
 341         changeListenerMock[0].check1();
 342         changeListenerMock[0].clear();
 343 
 344         // remove invalidation if array is empty
 345         helper = ListListenerHelper.addListener(helper, changeListenerMock[1]);
 346         helper = ListListenerHelper.removeListener(helper, invalidationListenerMock[0]);
 347         ListListenerHelper.fireValueChangedEvent(helper, change);
 348         invalidationListenerMock[0].check(null, 0);
 349         changeListenerMock[0].check1();
 350         changeListenerMock[1].check1();
 351         changeListenerMock[0].clear();
 352         changeListenerMock[1].clear();
 353 
 354         // remove last invalidation with two change
 355         helper = ListListenerHelper.addListener(helper, invalidationListenerMock[0]);
 356         helper = ListListenerHelper.removeListener(helper, invalidationListenerMock[0]);
 357         ListListenerHelper.fireValueChangedEvent(helper, change);
 358         invalidationListenerMock[0].check(null, 0);
 359         changeListenerMock[0].check1();
 360         changeListenerMock[1].check1();
 361     }
 362 
 363 
 364     @Test
 365     public void testGeneric_RemoveInvalidationInPulse() {
 366         final ListChangeListener<Object> removeListener = new ListChangeListener<Object>() {
 367             int counter;
 368             @Override
 369             public void onChanged(Change<? extends Object> c) {
 370                 helper = ListListenerHelper.removeListener(helper, invalidationListenerMock[counter++]);
 371             }
 372         };
 373         helper = ListListenerHelper.addListener(helper, changeListenerMock[0]);
 374         helper = ListListenerHelper.addListener(helper, invalidationListenerMock[0]);
 375         helper = ListListenerHelper.addListener(helper, invalidationListenerMock[3]);
 376         helper = ListListenerHelper.addListener(helper, invalidationListenerMock[1]);
 377         helper = ListListenerHelper.addListener(helper, invalidationListenerMock[2]);
 378 
 379         helper = ListListenerHelper.addListener(helper, removeListener);
 380         ListListenerHelper.fireValueChangedEvent(helper, change);
 381         helper = ListListenerHelper.removeListener(helper, removeListener);
 382         resetAllListeners();
 383         ListListenerHelper.fireValueChangedEvent(helper, change);
 384         invalidationListenerMock[0].check(null, 0);
 385         invalidationListenerMock[3].check(list, 1);
 386         invalidationListenerMock[1].check(list, 1);
 387         invalidationListenerMock[2].check(list, 1);
 388 
 389         helper = ListListenerHelper.addListener(helper, removeListener);
 390         ListListenerHelper.fireValueChangedEvent(helper, change);
 391         helper = ListListenerHelper.removeListener(helper, removeListener);
 392         resetAllListeners();
 393         ListListenerHelper.fireValueChangedEvent(helper, change);
 394         invalidationListenerMock[0].check(null, 0);
 395         invalidationListenerMock[3].check(list, 1);
 396         invalidationListenerMock[1].check(null, 0);
 397         invalidationListenerMock[2].check(list, 1);
 398 
 399         helper = ListListenerHelper.addListener(helper, removeListener);
 400         ListListenerHelper.fireValueChangedEvent(helper, change);
 401         helper = ListListenerHelper.removeListener(helper, removeListener);
 402         resetAllListeners();
 403         ListListenerHelper.fireValueChangedEvent(helper, change);
 404         invalidationListenerMock[0].check(null, 0);
 405         invalidationListenerMock[3].check(list, 1);
 406         invalidationListenerMock[1].check(null, 0);
 407         invalidationListenerMock[2].check(null, 0);
 408 
 409         helper = ListListenerHelper.addListener(helper, removeListener);
 410         ListListenerHelper.fireValueChangedEvent(helper, change);
 411         helper = ListListenerHelper.removeListener(helper, removeListener);
 412         resetAllListeners();
 413         ListListenerHelper.fireValueChangedEvent(helper, change);
 414         invalidationListenerMock[0].check(null, 0);
 415         invalidationListenerMock[3].check(null, 0);
 416         invalidationListenerMock[1].check(null, 0);
 417         invalidationListenerMock[2].check(null, 0);
 418     }
 419 
 420     @Test
 421     public void testGeneric_AddChange() {
 422         helper = ListListenerHelper.addListener(helper, invalidationListenerMock[0]);
 423         helper = ListListenerHelper.addListener(helper, invalidationListenerMock[0]);
 424 
 425         // first change listener creates the array
 426         helper = ListListenerHelper.addListener(helper, changeListenerMock[0]);
 427         ListListenerHelper.fireValueChangedEvent(helper, change);
 428         changeListenerMock[0].check1();
 429         changeListenerMock[0].clear();
 430 
 431         // second and third change listener enlarge the array
 432         helper = ListListenerHelper.addListener(helper, changeListenerMock[1]);
 433         helper = ListListenerHelper.addListener(helper, changeListenerMock[2]);
 434         ListListenerHelper.fireValueChangedEvent(helper, change);
 435         changeListenerMock[0].check1();
 436         changeListenerMock[1].check1();
 437         changeListenerMock[2].check1();
 438         resetAllListeners();
 439 
 440         // fourth change listener fits into the array
 441         helper = ListListenerHelper.addListener(helper, changeListenerMock[3]);
 442         ListListenerHelper.fireValueChangedEvent(helper, change);
 443         changeListenerMock[0].check1();
 444         changeListenerMock[1].check1();
 445         changeListenerMock[2].check1();
 446         changeListenerMock[3].check1();
 447     }
 448 
 449     @Test
 450     public void testGeneric_AddChangeInPulse() {
 451         final InvalidationListener addListener = new InvalidationListener() {
 452             int counter;
 453             @Override
 454             public void invalidated(Observable observable) {
 455                 helper = ListListenerHelper.addListener(helper, changeListenerMock[counter++]);
 456 
 457             }
 458         };
 459         helper = ListListenerHelper.addListener(helper, invalidationListenerMock[0]);
 460 
 461         helper = ListListenerHelper.addListener(helper, addListener);
 462         ListListenerHelper.fireValueChangedEvent(helper, change);
 463         helper = ListListenerHelper.removeListener(helper, addListener);
 464         resetAllListeners();
 465         ListListenerHelper.fireValueChangedEvent(helper, change);
 466         changeListenerMock[0].check1();
 467         changeListenerMock[1].check0();
 468         changeListenerMock[2].check0();
 469         changeListenerMock[3].check0();
 470 
 471         helper = ListListenerHelper.addListener(helper, addListener);
 472         ListListenerHelper.fireValueChangedEvent(helper, change);
 473         helper = ListListenerHelper.removeListener(helper, addListener);
 474         resetAllListeners();
 475         ListListenerHelper.fireValueChangedEvent(helper, change);
 476         changeListenerMock[0].check1();
 477         changeListenerMock[1].check1();
 478         changeListenerMock[2].check0();
 479         changeListenerMock[3].check0();
 480 
 481         helper = ListListenerHelper.addListener(helper, addListener);
 482         ListListenerHelper.fireValueChangedEvent(helper, change);
 483         helper = ListListenerHelper.removeListener(helper, addListener);
 484         resetAllListeners();
 485         ListListenerHelper.fireValueChangedEvent(helper, change);
 486         changeListenerMock[0].check1();
 487         changeListenerMock[1].check1();
 488         changeListenerMock[2].check1();
 489         changeListenerMock[3].check0();
 490 
 491         helper = ListListenerHelper.addListener(helper, addListener);
 492         ListListenerHelper.fireValueChangedEvent(helper, change);
 493         helper = ListListenerHelper.removeListener(helper, addListener);
 494         resetAllListeners();
 495         ListListenerHelper.fireValueChangedEvent(helper, change);
 496         changeListenerMock[0].check1();
 497         changeListenerMock[1].check1();
 498         changeListenerMock[2].check1();
 499         changeListenerMock[3].check1();
 500     }
 501 
 502     @Test
 503     public void testGeneric_RemoveChange() {
 504         helper = ListListenerHelper.addListener(helper, changeListenerMock[0]);
 505         helper = ListListenerHelper.addListener(helper, changeListenerMock[1]);
 506         helper = ListListenerHelper.addListener(helper, changeListenerMock[2]);
 507         helper = ListListenerHelper.addListener(helper, changeListenerMock[3]);
 508 
 509         // remove first element
 510         helper = ListListenerHelper.removeListener(helper, changeListenerMock[0]);
 511         ListListenerHelper.fireValueChangedEvent(helper, change);
 512         changeListenerMock[0].check0();
 513         changeListenerMock[1].check1();
 514         changeListenerMock[2].check1();
 515         changeListenerMock[3].check1();
 516         resetAllListeners();
 517 
 518         // remove middle element
 519         helper = ListListenerHelper.removeListener(helper, changeListenerMock[2]);
 520         ListListenerHelper.fireValueChangedEvent(helper, change);
 521         changeListenerMock[0].check0();
 522         changeListenerMock[1].check1();
 523         changeListenerMock[2].check0();
 524         changeListenerMock[3].check1();
 525         resetAllListeners();
 526 
 527         // remove last element
 528         helper = ListListenerHelper.removeListener(helper, changeListenerMock[3]);
 529         ListListenerHelper.fireValueChangedEvent(helper, change);
 530         changeListenerMock[0].check0();
 531         changeListenerMock[1].check1();
 532         changeListenerMock[2].check0();
 533         changeListenerMock[3].check0();
 534         resetAllListeners();
 535 
 536         // remove last change with single invalidation
 537         helper = ListListenerHelper.addListener(helper, invalidationListenerMock[0]);
 538         helper = ListListenerHelper.removeListener(helper, changeListenerMock[1]);
 539         ListListenerHelper.fireValueChangedEvent(helper, change);
 540         invalidationListenerMock[0].check(list, 1);
 541         changeListenerMock[1].check0();
 542         changeListenerMock[1].clear();
 543 
 544         // remove change if array is empty
 545         helper = ListListenerHelper.addListener(helper, invalidationListenerMock[1]);
 546         helper = ListListenerHelper.removeListener(helper, changeListenerMock[0]);
 547         ListListenerHelper.fireValueChangedEvent(helper, change);
 548         invalidationListenerMock[0].check(list, 1);
 549         invalidationListenerMock[1].check(list, 1);
 550         changeListenerMock[0].check0();
 551         changeListenerMock[0].clear();
 552 
 553         // remove last change with two invalidation
 554         helper = ListListenerHelper.addListener(helper, changeListenerMock[0]);
 555         helper = ListListenerHelper.removeListener(helper, changeListenerMock[0]);
 556         ListListenerHelper.fireValueChangedEvent(helper, change);
 557         invalidationListenerMock[0].check(list, 1);
 558         invalidationListenerMock[1].check(list, 1);
 559         changeListenerMock[0].check0();
 560         changeListenerMock[0].clear();
 561     }
 562 
 563 
 564     @Test
 565     public void testGeneric_RemoveChangeInPulse() {
 566         final InvalidationListener removeListener = new InvalidationListener() {
 567             int counter;
 568             @Override
 569             public void invalidated(Observable observable) {
 570                 helper = ListListenerHelper.removeListener(helper, changeListenerMock[counter++]);
 571             }
 572         };
 573         helper = ListListenerHelper.addListener(helper, invalidationListenerMock[0]);
 574         helper = ListListenerHelper.addListener(helper, changeListenerMock[0]);
 575         helper = ListListenerHelper.addListener(helper, changeListenerMock[3]);
 576         helper = ListListenerHelper.addListener(helper, changeListenerMock[1]);
 577         helper = ListListenerHelper.addListener(helper, changeListenerMock[2]);
 578 
 579         helper = ListListenerHelper.addListener(helper, removeListener);
 580         ListListenerHelper.fireValueChangedEvent(helper, change);
 581         helper = ListListenerHelper.removeListener(helper, removeListener);
 582         resetAllListeners();
 583         ListListenerHelper.fireValueChangedEvent(helper, change);
 584         changeListenerMock[0].check0();
 585         changeListenerMock[3].check1();
 586         changeListenerMock[1].check1();
 587         changeListenerMock[2].check1();
 588 
 589         helper = ListListenerHelper.addListener(helper, removeListener);
 590         ListListenerHelper.fireValueChangedEvent(helper, change);
 591         helper = ListListenerHelper.removeListener(helper, removeListener);
 592         resetAllListeners();
 593         ListListenerHelper.fireValueChangedEvent(helper, change);
 594         changeListenerMock[0].check0();
 595         changeListenerMock[3].check1();
 596         changeListenerMock[1].check0();
 597         changeListenerMock[2].check1();
 598 
 599         helper = ListListenerHelper.addListener(helper, removeListener);
 600         ListListenerHelper.fireValueChangedEvent(helper, change);
 601         helper = ListListenerHelper.removeListener(helper, removeListener);
 602         resetAllListeners();
 603         ListListenerHelper.fireValueChangedEvent(helper, change);
 604         changeListenerMock[0].check0();
 605         changeListenerMock[3].check1();
 606         changeListenerMock[1].check0();
 607         changeListenerMock[2].check0();
 608 
 609         helper = ListListenerHelper.addListener(helper, removeListener);
 610         ListListenerHelper.fireValueChangedEvent(helper, change);
 611         helper = ListListenerHelper.removeListener(helper, removeListener);
 612         resetAllListeners();
 613         ListListenerHelper.fireValueChangedEvent(helper, change);
 614         changeListenerMock[0].check0();
 615         changeListenerMock[3].check0();
 616         changeListenerMock[1].check0();
 617         changeListenerMock[2].check0();
 618     }
 619 
 620 
 621     @Test
 622     public void testExceptionNotPropagatedFromSingleInvalidation() {
 623         helper = ListListenerHelper.addListener(helper,(Observable o) -> {throw new RuntimeException();});
 624         ListListenerHelper.fireValueChangedEvent(helper, change);
 625     }
 626 
 627     @Test
 628     public void testExceptionNotPropagatedFromMultipleInvalidation() {
 629         BitSet called = new BitSet();
 630 
 631         helper = ListListenerHelper.addListener(helper, (Observable o) -> {called.set(0); throw new RuntimeException();});
 632         helper = ListListenerHelper.addListener(helper, (Observable o) -> {called.set(1); throw new RuntimeException();});
 633 
 634         ListListenerHelper.fireValueChangedEvent(helper, change);
 635 
 636         assertTrue(called.get(0));
 637         assertTrue(called.get(1));
 638     }
 639 
 640     @Test
 641     public void testExceptionNotPropagatedFromSingleChange() {
 642         helper = ListListenerHelper.addListener(helper, (ListChangeListener.Change<? extends Object> c) -> {
 643             throw new RuntimeException();
 644         });
 645         ListListenerHelper.fireValueChangedEvent(helper, change);
 646     }
 647 
 648     @Test
 649     public void testExceptionNotPropagatedFromMultipleChange() {
 650         BitSet called = new BitSet();
 651 
 652         helper = ListListenerHelper.addListener(helper, (ListChangeListener.Change<? extends Object> c) -> {called.set(0); throw new RuntimeException();});
 653         helper = ListListenerHelper.addListener(helper, (ListChangeListener.Change<? extends Object> c) -> {called.set(1); throw new RuntimeException();});
 654         ListListenerHelper.fireValueChangedEvent(helper, change);
 655 
 656         assertTrue(called.get(0));
 657         assertTrue(called.get(1));
 658     }
 659 
 660     @Test
 661     public void testExceptionNotPropagatedFromMultipleChangeAndInvalidation() {
 662         BitSet called = new BitSet();
 663 
 664         helper = ListListenerHelper.addListener(helper, (ListChangeListener.Change<? extends Object> c) -> {called.set(0); throw new RuntimeException();});
 665         helper = ListListenerHelper.addListener(helper, (ListChangeListener.Change<? extends Object> c) -> {called.set(1); throw new RuntimeException();});
 666         helper = ListListenerHelper.addListener(helper, (Observable o) -> {called.set(2); throw new RuntimeException();});
 667         helper = ListListenerHelper.addListener(helper, (Observable o) -> {called.set(3); throw new RuntimeException();});
 668         ListListenerHelper.fireValueChangedEvent(helper, change);
 669 
 670         assertTrue(called.get(0));
 671         assertTrue(called.get(1));
 672         assertTrue(called.get(2));
 673         assertTrue(called.get(3));
 674     }
 675 
 676     @Test
 677     public void testExceptionHandledByThreadUncaughtHandlerInSingleInvalidation() {
 678         AtomicBoolean called = new AtomicBoolean(false);
 679 
 680         Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.set(true));
 681 
 682         helper = ListListenerHelper.addListener(helper,(Observable o) -> {throw new RuntimeException();});
 683         ListListenerHelper.fireValueChangedEvent(helper, change);
 684 
 685         assertTrue(called.get());
 686     }
 687 
 688 
 689     @Test
 690     public void testExceptionHandledByThreadUncaughtHandlerInMultipleInvalidation() {
 691         AtomicInteger called = new AtomicInteger(0);
 692 
 693         Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.incrementAndGet());
 694 
 695         helper = ListListenerHelper.addListener(helper, (Observable o) -> {throw new RuntimeException();});
 696         helper = ListListenerHelper.addListener(helper, (Observable o) -> {throw new RuntimeException();});
 697         ListListenerHelper.fireValueChangedEvent(helper, change);
 698 
 699         assertEquals(2, called.get());
 700     }
 701 
 702     @Test
 703     public void testExceptionHandledByThreadUncaughtHandlerInSingleChange() {
 704         AtomicBoolean called = new AtomicBoolean(false);
 705 
 706         Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.set(true));
 707         helper = ListListenerHelper.addListener(helper, (ListChangeListener.Change<? extends Object> c) -> {throw new RuntimeException();});
 708         ListListenerHelper.fireValueChangedEvent(helper, change);
 709 
 710         assertTrue(called.get());
 711     }
 712 
 713     @Test
 714     public void testExceptionHandledByThreadUncaughtHandlerInMultipleChange() {
 715         AtomicInteger called = new AtomicInteger(0);
 716 
 717         Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.incrementAndGet());
 718 
 719         helper = ListListenerHelper.addListener(helper, (ListChangeListener.Change<? extends Object> c) -> {throw new RuntimeException();});
 720         helper = ListListenerHelper.addListener(helper, (ListChangeListener.Change<? extends Object> c) -> {throw new RuntimeException();});
 721         ListListenerHelper.fireValueChangedEvent(helper, change);
 722 
 723         assertEquals(2, called.get());
 724     }
 725 
 726     @Test
 727     public void testExceptionHandledByThreadUncaughtHandlerInMultipleChangeAndInvalidation() {
 728         AtomicInteger called = new AtomicInteger(0);
 729 
 730         Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.incrementAndGet());
 731 
 732         helper = ListListenerHelper.addListener(helper, (ListChangeListener.Change<? extends Object> c) -> { throw new RuntimeException();});
 733         helper = ListListenerHelper.addListener(helper, (ListChangeListener.Change<? extends Object> c) -> { throw new RuntimeException();});
 734         helper = ListListenerHelper.addListener(helper, (Observable o) -> { throw new RuntimeException();});
 735         helper = ListListenerHelper.addListener(helper, (Observable o) -> {throw new RuntimeException();});
 736         ListListenerHelper.fireValueChangedEvent(helper, change);
 737 
 738         assertEquals(4, called.get());
 739     }
 740 
 741 }