changeset 6845:669f56bc38e9

RT-36769 Fix for RT-33633 breaks the ability for application developers to deal with exceptions properly
author Martin Sladecek <martin.sladecek@oracle.com>
date Tue, 22 Apr 2014 13:18:57 +0200
parents 8c3083ca5111
children da7406208784
files modules/base/src/main/java/com/sun/javafx/binding/ExpressionHelper.java modules/base/src/main/java/com/sun/javafx/collections/ArrayListenerHelper.java modules/base/src/main/java/com/sun/javafx/collections/ListListenerHelper.java modules/base/src/main/java/com/sun/javafx/collections/MapListenerHelper.java modules/base/src/main/java/com/sun/javafx/collections/SetListenerHelper.java modules/base/src/test/java/com/sun/javafx/binding/ExpressionHelperTest.java modules/base/src/test/java/com/sun/javafx/collections/ListListenerHelperTest.java modules/base/src/test/java/com/sun/javafx/collections/MapListenerHelperTest.java modules/base/src/test/java/com/sun/javafx/collections/SetListenerHelperTest.java
diffstat 9 files changed, 297 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/modules/base/src/main/java/com/sun/javafx/binding/ExpressionHelper.java	Tue Apr 22 08:52:01 2014 +0200
+++ b/modules/base/src/main/java/com/sun/javafx/binding/ExpressionHelper.java	Tue Apr 22 13:18:57 2014 +0200
@@ -136,7 +136,7 @@
             try {
                 listener.invalidated(observable);
             } catch (Exception e) {
-                PlatformLogger.getLogger("beans").warning("Exception in InvalidationListener", e);
+                Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
             }
         }
     }
@@ -181,7 +181,7 @@
                 try {
                     listener.changed(observable, oldValue, currentValue);
                 } catch (Exception e) {
-                    PlatformLogger.getLogger("beans").warning("Exception in ChangeListener", e);
+                    Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
                 }
             }
         }
@@ -348,7 +348,7 @@
                     try {
                         curInvalidationList[i].invalidated(observable);
                     } catch (Exception e) {
-                        PlatformLogger.getLogger("beans").warning("Exception in InvalidationListener", e);
+                        Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
                     }
                 }
                 if (curChangeSize > 0) {
@@ -360,7 +360,7 @@
                             try {
                                 curChangeList[i].changed(observable, oldValue, currentValue);
                             } catch (Exception e) {
-                                PlatformLogger.getLogger("beans").warning("Exception in ChangeListener", e);
+                                Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
                             }
                         }
                     }
--- a/modules/base/src/main/java/com/sun/javafx/collections/ArrayListenerHelper.java	Tue Apr 22 08:52:01 2014 +0200
+++ b/modules/base/src/main/java/com/sun/javafx/collections/ArrayListenerHelper.java	Tue Apr 22 13:18:57 2014 +0200
@@ -130,7 +130,7 @@
             try {
                 listener.invalidated(observable);
             } catch (Exception e) {
-                PlatformLogger.getLogger("collections").warning("Exception in InvalidationListener", e);
+                Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
             }
         }
     }
@@ -169,7 +169,7 @@
             try {
                 listener.onChanged(observable, sizeChanged, from, to);
             } catch (Exception e) {
-                PlatformLogger.getLogger("collections").warning("Exception in ArrayChangeListener", e);
+                Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
             }
         }
     }
@@ -329,14 +329,14 @@
                     try {
                         curInvalidationList[i].invalidated(observable);
                     } catch (Exception e) {
-                        PlatformLogger.getLogger("collections").warning("Exception in InvalidationListener", e);
+                        Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
                     }
                 }
                 for (int i = 0; i < curChangeSize; i++) {
                     try {
                         curChangeList[i].onChanged(observable, sizeChanged, from, to);
                     } catch (Exception e) {
-                        PlatformLogger.getLogger("collections").warning("Exception in ArrayChangeListener", e);
+                        Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
                     }
                 }
             } finally {
--- a/modules/base/src/main/java/com/sun/javafx/collections/ListListenerHelper.java	Tue Apr 22 08:52:01 2014 +0200
+++ b/modules/base/src/main/java/com/sun/javafx/collections/ListListenerHelper.java	Tue Apr 22 13:18:57 2014 +0200
@@ -125,7 +125,7 @@
             try {
             listener.invalidated(change.getList());
             } catch (Exception e) {
-                PlatformLogger.getLogger("collections").warning("Exception in InvalidationListener", e);
+                Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
             }
         }
     }
@@ -163,7 +163,7 @@
             try {
                 listener.onChanged(change);
             } catch (Exception e) {
-                PlatformLogger.getLogger("collections").warning("Exception in ListChangeListener", e);
+                Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
             }
         }
     }
@@ -320,7 +320,7 @@
                     try {
                         curInvalidationList[i].invalidated(change.getList());
                     } catch (Exception e) {
-                        PlatformLogger.getLogger("collections").warning("Exception in InvalidationListener", e);
+                        Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
                     }
                 }
                 for (int i = 0; i < curChangeSize; i++) {
@@ -328,7 +328,7 @@
                     try {
                         curChangeList[i].onChanged(change);
                     } catch (Exception e) {
-                        PlatformLogger.getLogger("collections").warning("Exception in ListChangeListenerListener", e);
+                        Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
                     }
                 }
             } finally {
--- a/modules/base/src/main/java/com/sun/javafx/collections/MapListenerHelper.java	Tue Apr 22 08:52:01 2014 +0200
+++ b/modules/base/src/main/java/com/sun/javafx/collections/MapListenerHelper.java	Tue Apr 22 13:18:57 2014 +0200
@@ -124,7 +124,7 @@
             try {
                 listener.invalidated(change.getMap());
             } catch (Exception e) {
-                PlatformLogger.getLogger("collections").warning("Exception in InvalidationListener", e);
+                Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
             }
         }
     }
@@ -162,7 +162,7 @@
             try {
                 listener.onChanged(change);
             } catch (Exception e) {
-                PlatformLogger.getLogger("collections").warning("Exception in MapChangeListener", e);
+                Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
             }
         }
     }
@@ -319,14 +319,14 @@
                     try {
                         curInvalidationList[i].invalidated(change.getMap());
                     } catch (Exception e) {
-                        PlatformLogger.getLogger("collections").warning("Exception in InvalidationListener", e);
+                        Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
                     }
                 }
                 for (int i = 0; i < curChangeSize; i++) {
                     try {
                         curChangeList[i].onChanged(change);
                     } catch (Exception e) {
-                        PlatformLogger.getLogger("collections").warning("Exception in MapChangeListener", e);
+                        Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
                     }
                 }
             } finally {
--- a/modules/base/src/main/java/com/sun/javafx/collections/SetListenerHelper.java	Tue Apr 22 08:52:01 2014 +0200
+++ b/modules/base/src/main/java/com/sun/javafx/collections/SetListenerHelper.java	Tue Apr 22 13:18:57 2014 +0200
@@ -124,7 +124,7 @@
             try {
                 listener.invalidated(change.getSet());
             } catch (Exception e) {
-                PlatformLogger.getLogger("collections").warning("Exception in InvalidationListener", e);
+                Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
             }
         }
     }
@@ -162,7 +162,7 @@
             try {
                 listener.onChanged(change);
             } catch (Exception e) {
-                PlatformLogger.getLogger("collections").warning("Exception in SetChangeListener", e);
+                Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
             }
         }
     }
@@ -319,14 +319,14 @@
                     try {
                         curInvalidationList[i].invalidated(change.getSet());
                     } catch (Exception e) {
-                        PlatformLogger.getLogger("collections").warning("Exception in InvalidationListener", e);
+                        Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
                     }
                 }
                 for (int i = 0; i < curChangeSize; i++) {
                     try {
                         curChangeList[i].onChanged(change);
                     } catch (Exception e) {
-                        PlatformLogger.getLogger("collections").warning("Exception in SetChangeListener", e);
+                        Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
                     }
                 }
             } finally {
--- a/modules/base/src/test/java/com/sun/javafx/binding/ExpressionHelperTest.java	Tue Apr 22 08:52:01 2014 +0200
+++ b/modules/base/src/test/java/com/sun/javafx/binding/ExpressionHelperTest.java	Tue Apr 22 13:18:57 2014 +0200
@@ -38,7 +38,10 @@
 import org.junit.Test;
 
 import java.util.BitSet;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
 public class ExpressionHelperTest {
@@ -570,4 +573,74 @@
         assertTrue(called.get(3));
     }
 
+    @Test
+    public void testExceptionHandledByThreadUncaughtHandlerInSingleInvalidation() {
+        AtomicBoolean called = new AtomicBoolean(false);
+
+        Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.set(true));
+
+        helper = ExpressionHelper.addListener(helper, observable,(o) -> {throw new RuntimeException();});
+        observable.set(null);
+        helper.fireValueChangedEvent();
+
+        assertTrue(called.get());
+    }
+
+
+    @Test
+    public void testExceptionHandledByThreadUncaughtHandlerInMultipleInvalidation() {
+        AtomicInteger called = new AtomicInteger(0);
+
+        Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.incrementAndGet());
+
+        helper = ExpressionHelper.addListener(helper, observable, (o) -> {throw new RuntimeException();});
+        helper = ExpressionHelper.addListener(helper, observable, (o) -> {throw new RuntimeException();});
+        observable.set(null);
+        helper.fireValueChangedEvent();
+
+        assertEquals(2, called.get());
+    }
+
+    @Test
+    public void testExceptionHandledByThreadUncaughtHandlerInSingleChange() {
+        AtomicBoolean called = new AtomicBoolean(false);
+
+        Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.set(true));
+        helper = ExpressionHelper.addListener(helper, observable, (value, o1, o2) -> {throw new RuntimeException();});
+        observable.set(null);
+        helper.fireValueChangedEvent();
+
+        assertTrue(called.get());
+    }
+
+    @Test
+    public void testExceptionHandledByThreadUncaughtHandlerInMultipleChange() {
+        AtomicInteger called = new AtomicInteger(0);
+
+        Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.incrementAndGet());
+
+        helper = ExpressionHelper.addListener(helper, observable, (value, o1, o2) -> {throw new RuntimeException();});
+        helper = ExpressionHelper.addListener(helper, observable, (value, o1, o2) -> {throw new RuntimeException();});
+        observable.set(null);
+        helper.fireValueChangedEvent();
+
+        assertEquals(2, called.get());
+    }
+
+    @Test
+    public void testExceptionHandledByThreadUncaughtHandlerInMultipleChangeAndInvalidation() {
+        AtomicInteger called = new AtomicInteger(0);
+
+        Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.incrementAndGet());
+
+        helper = ExpressionHelper.addListener(helper, observable, (value, o1, o2) -> { throw new RuntimeException();});
+        helper = ExpressionHelper.addListener(helper, observable, (value, o1, o2) -> { throw new RuntimeException();});
+        helper = ExpressionHelper.addListener(helper, observable, (o) -> { throw new RuntimeException();});
+        helper = ExpressionHelper.addListener(helper, observable, (o) -> {throw new RuntimeException();});
+        observable.set(null);
+        helper.fireValueChangedEvent();
+
+        assertEquals(4, called.get());
+    }
+
 }
--- a/modules/base/src/test/java/com/sun/javafx/collections/ListListenerHelperTest.java	Tue Apr 22 08:52:01 2014 +0200
+++ b/modules/base/src/test/java/com/sun/javafx/collections/ListListenerHelperTest.java	Tue Apr 22 13:18:57 2014 +0200
@@ -39,7 +39,10 @@
 import org.junit.Test;
 
 import java.util.BitSet;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
@@ -670,4 +673,69 @@
         assertTrue(called.get(3));
     }
 
+    @Test
+    public void testExceptionHandledByThreadUncaughtHandlerInSingleInvalidation() {
+        AtomicBoolean called = new AtomicBoolean(false);
+
+        Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.set(true));
+
+        helper = ListListenerHelper.addListener(helper,(Observable o) -> {throw new RuntimeException();});
+        helper.fireValueChangedEvent(change);
+
+        assertTrue(called.get());
+    }
+
+
+    @Test
+    public void testExceptionHandledByThreadUncaughtHandlerInMultipleInvalidation() {
+        AtomicInteger called = new AtomicInteger(0);
+
+        Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.incrementAndGet());
+
+        helper = ListListenerHelper.addListener(helper, (Observable o) -> {throw new RuntimeException();});
+        helper = ListListenerHelper.addListener(helper, (Observable o) -> {throw new RuntimeException();});
+        helper.fireValueChangedEvent(change);
+
+        assertEquals(2, called.get());
+    }
+
+    @Test
+    public void testExceptionHandledByThreadUncaughtHandlerInSingleChange() {
+        AtomicBoolean called = new AtomicBoolean(false);
+
+        Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.set(true));
+        helper = ListListenerHelper.addListener(helper, (ListChangeListener.Change<? extends Object> c) -> {throw new RuntimeException();});
+        helper.fireValueChangedEvent(change);
+
+        assertTrue(called.get());
+    }
+
+    @Test
+    public void testExceptionHandledByThreadUncaughtHandlerInMultipleChange() {
+        AtomicInteger called = new AtomicInteger(0);
+
+        Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.incrementAndGet());
+
+        helper = ListListenerHelper.addListener(helper, (ListChangeListener.Change<? extends Object> c) -> {throw new RuntimeException();});
+        helper = ListListenerHelper.addListener(helper, (ListChangeListener.Change<? extends Object> c) -> {throw new RuntimeException();});
+        helper.fireValueChangedEvent(change);
+
+        assertEquals(2, called.get());
+    }
+
+    @Test
+    public void testExceptionHandledByThreadUncaughtHandlerInMultipleChangeAndInvalidation() {
+        AtomicInteger called = new AtomicInteger(0);
+
+        Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.incrementAndGet());
+
+        helper = ListListenerHelper.addListener(helper, (ListChangeListener.Change<? extends Object> c) -> { throw new RuntimeException();});
+        helper = ListListenerHelper.addListener(helper, (ListChangeListener.Change<? extends Object> c) -> { throw new RuntimeException();});
+        helper = ListListenerHelper.addListener(helper, (Observable o) -> { throw new RuntimeException();});
+        helper = ListListenerHelper.addListener(helper, (Observable o) -> {throw new RuntimeException();});
+        helper.fireValueChangedEvent(change);
+
+        assertEquals(4, called.get());
+    }
+
 }
--- a/modules/base/src/test/java/com/sun/javafx/collections/MapListenerHelperTest.java	Tue Apr 22 08:52:01 2014 +0200
+++ b/modules/base/src/test/java/com/sun/javafx/collections/MapListenerHelperTest.java	Tue Apr 22 13:18:57 2014 +0200
@@ -36,6 +36,8 @@
 import org.junit.Test;
 
 import java.util.BitSet;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -668,4 +670,71 @@
         assertTrue(called.get(2));
         assertTrue(called.get(3));
     }
+
+
+    @Test
+    public void testExceptionHandledByThreadUncaughtHandlerInSingleInvalidation() {
+        AtomicBoolean called = new AtomicBoolean(false);
+
+        Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.set(true));
+
+        helper = MapListenerHelper.addListener(helper,(Observable o) -> {throw new RuntimeException();});
+        helper.fireValueChangedEvent(change);
+
+        assertTrue(called.get());
+    }
+
+
+    @Test
+    public void testExceptionHandledByThreadUncaughtHandlerInMultipleInvalidation() {
+        AtomicInteger called = new AtomicInteger(0);
+
+        Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.incrementAndGet());
+
+        helper = MapListenerHelper.addListener(helper, (Observable o) -> {throw new RuntimeException();});
+        helper = MapListenerHelper.addListener(helper, (Observable o) -> {throw new RuntimeException();});
+        helper.fireValueChangedEvent(change);
+
+        assertEquals(2, called.get());
+    }
+
+    @Test
+    public void testExceptionHandledByThreadUncaughtHandlerInSingleChange() {
+        AtomicBoolean called = new AtomicBoolean(false);
+
+        Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.set(true));
+        helper = MapListenerHelper.addListener(helper, (MapChangeListener.Change<? extends Object, ? extends Object> c) -> {throw new RuntimeException();});
+        helper.fireValueChangedEvent(change);
+
+        assertTrue(called.get());
+    }
+
+    @Test
+    public void testExceptionHandledByThreadUncaughtHandlerInMultipleChange() {
+        AtomicInteger called = new AtomicInteger(0);
+
+        Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.incrementAndGet());
+
+        helper = MapListenerHelper.addListener(helper, (MapChangeListener.Change<? extends Object, ? extends Object> c) -> {throw new RuntimeException();});
+        helper = MapListenerHelper.addListener(helper, (MapChangeListener.Change<? extends Object, ? extends Object> c) -> {throw new RuntimeException();});
+        helper.fireValueChangedEvent(change);
+
+        assertEquals(2, called.get());
+    }
+
+    @Test
+    public void testExceptionHandledByThreadUncaughtHandlerInMultipleChangeAndInvalidation() {
+        AtomicInteger called = new AtomicInteger(0);
+
+        Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.incrementAndGet());
+
+        helper = MapListenerHelper.addListener(helper, (MapChangeListener.Change<? extends Object, ? extends Object> c) -> { throw new RuntimeException();});
+        helper = MapListenerHelper.addListener(helper, (MapChangeListener.Change<? extends Object, ? extends Object> c) -> { throw new RuntimeException();});
+        helper = MapListenerHelper.addListener(helper, (Observable o) -> { throw new RuntimeException();});
+        helper = MapListenerHelper.addListener(helper, (Observable o) -> {throw new RuntimeException();});
+        helper.fireValueChangedEvent(change);
+
+        assertEquals(4, called.get());
+    }
+
 }
--- a/modules/base/src/test/java/com/sun/javafx/collections/SetListenerHelperTest.java	Tue Apr 22 08:52:01 2014 +0200
+++ b/modules/base/src/test/java/com/sun/javafx/collections/SetListenerHelperTest.java	Tue Apr 22 13:18:57 2014 +0200
@@ -38,6 +38,8 @@
 import org.junit.Test;
 
 import java.util.BitSet;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -672,4 +674,69 @@
     }
 
 
+    @Test
+    public void testExceptionHandledByThreadUncaughtHandlerInSingleInvalidation() {
+        AtomicBoolean called = new AtomicBoolean(false);
+
+        Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.set(true));
+
+        helper = SetListenerHelper.addListener(helper,(Observable o) -> {throw new RuntimeException();});
+        helper.fireValueChangedEvent(change);
+
+        assertTrue(called.get());
+    }
+
+
+    @Test
+    public void testExceptionHandledByThreadUncaughtHandlerInMultipleInvalidation() {
+        AtomicInteger called = new AtomicInteger(0);
+
+        Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.incrementAndGet());
+
+        helper = SetListenerHelper.addListener(helper, (Observable o) -> {throw new RuntimeException();});
+        helper = SetListenerHelper.addListener(helper, (Observable o) -> {throw new RuntimeException();});
+        helper.fireValueChangedEvent(change);
+
+        assertEquals(2, called.get());
+    }
+
+    @Test
+    public void testExceptionHandledByThreadUncaughtHandlerInSingleChange() {
+        AtomicBoolean called = new AtomicBoolean(false);
+
+        Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.set(true));
+        helper = SetListenerHelper.addListener(helper, (SetChangeListener.Change<? extends Object> c) -> {throw new RuntimeException();});
+        helper.fireValueChangedEvent(change);
+
+        assertTrue(called.get());
+    }
+
+    @Test
+    public void testExceptionHandledByThreadUncaughtHandlerInMultipleChange() {
+        AtomicInteger called = new AtomicInteger(0);
+
+        Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.incrementAndGet());
+
+        helper = SetListenerHelper.addListener(helper, (SetChangeListener.Change<? extends Object> c) -> {throw new RuntimeException();});
+        helper = SetListenerHelper.addListener(helper, (SetChangeListener.Change<? extends Object> c) -> {throw new RuntimeException();});
+        helper.fireValueChangedEvent(change);
+
+        assertEquals(2, called.get());
+    }
+
+    @Test
+    public void testExceptionHandledByThreadUncaughtHandlerInMultipleChangeAndInvalidation() {
+        AtomicInteger called = new AtomicInteger(0);
+
+        Thread.currentThread().setUncaughtExceptionHandler((t, e) -> called.incrementAndGet());
+
+        helper = SetListenerHelper.addListener(helper, (SetChangeListener.Change<? extends Object> c) -> { throw new RuntimeException();});
+        helper = SetListenerHelper.addListener(helper, (SetChangeListener.Change<? extends Object> c) -> { throw new RuntimeException();});
+        helper = SetListenerHelper.addListener(helper, (Observable o) -> { throw new RuntimeException();});
+        helper = SetListenerHelper.addListener(helper, (Observable o) -> {throw new RuntimeException();});
+        helper.fireValueChangedEvent(change);
+
+        assertEquals(4, called.get());
+    }
+
 }