Computation implements ReactiveValueChangeListener { private void invalidate() { invalidated = true; clearDependencies(); if (!stopped) { Reactive.addFlushListener(this::recompute); } if (invalidateListeners.size() != 0) { JsSet<InvalidateListener> oldListeners = invalidateListeners; invalidateListeners = JsCollections.set(); InvalidateEvent invalidateEvent = new InvalidateEvent(this); oldListeners.forEach( listener -> listener.onInvalidate(invalidateEvent)); } } Computation(); void addDependency(ReactiveValue dependency); @Override void onValueChange(ReactiveValueChangeEvent changeEvent); void stop(); boolean isInvalidated(); void recompute(); void onNextInvalidate(InvalidateListener listener);  }
@Test public void testRerunIfDirty() { CountingComputation computation = new CountingComputation(router); Assert.assertEquals("Initial compute should not trigger before flush", 0, computation.getCount()); Reactive.flush(); Assert.assertEquals("Flush should trigger initial compute", 1, computation.getCount()); Reactive.flush(); Assert.assertEquals( "Another recompute should not trigger since dependency has not changed", 1, computation.getCount()); router.invalidate(); Assert.assertEquals( "Invalidation should not trigger recompute until flush", 1, computation.getCount()); Reactive.flush(); Assert.assertEquals("Should recompute after flush after invalidation", 2, computation.getCount()); }
@Test public void testChangeDependencies() { TestReactiveEventRouter otherRouter = new TestReactiveEventRouter(); Reactive.runWhenDependenciesChange(() -> { int count = computeCount.incrementAndGet(); if (count % 2 == 0) { router.registerRead(); } else { otherRouter.registerRead(); } }); Reactive.flush(); router.invalidate(); Reactive.flush(); Assert.assertEquals("Invalidating router should not cause recompute", 1, computeCount.get()); otherRouter.invalidate(); Reactive.flush(); Assert.assertEquals("Invalidating otherRouter should cause recompute", 2, computeCount.get()); otherRouter.invalidate(); Reactive.flush(); Assert.assertEquals( "Invalidating otherRouter should not cause recompute", 2, computeCount.get()); router.invalidate(); Reactive.flush(); Assert.assertEquals("Invalidating router should cause recompute", 3, computeCount.get()); }
@Test public void testReactiveListeners() { Reactive.runWhenDependenciesChange(() -> { if (computeCount.get() == 0) { router.addListener(event -> computeCount.incrementAndGet()); router.registerRead(); } }); Reactive.flush(); Assert.assertEquals( "First flush registers listener, but doesn't increment count", 0, computeCount.get()); router.invalidate(); Assert.assertEquals("Listener is fired right away, but removed", 1, computeCount.get()); router.invalidate(); Assert.assertEquals( "Listner has been removed, so count isn't increased", 1, computeCount.get()); Reactive.flush(); Assert.assertEquals("Listener is not added again", 1, computeCount.get()); router.invalidate(); Assert.assertEquals("Listner has not been added again", 1, computeCount.get()); }
@Test public void escapeReactive() { Reactive.runWhenDependenciesChange(() -> { computeCount.incrementAndGet(); Reactive.runWithComputation(null, router::registerRead); }); Reactive.flush(); Assert.assertEquals(1, computeCount.get()); router.invalidate(); Reactive.flush(); Assert.assertEquals("No dependency was registered", 1, computeCount.get()); }