@@ -0,0 +1,3329 @@
+package arez.integration;%0A%0Aimport arez.Arez;%0Aimport arez.ArezContext;%0Aimport arez.ComputedValue;%0Aimport arez.Disposable;%0Aimport arez.ObservableValue;%0Aimport arez.Observer;%0Aimport arez.Priority;%0Aimport arez.Procedure;%0Aimport arez.SafeFunction;%0Aimport arez.SafeProcedure;%0Aimport java.util.concurrent.atomic.AtomicBoolean;%0Aimport java.util.concurrent.atomic.AtomicInteger;%0Aimport javax.annotation.Nonnull;%0Aimport org.realityforge.guiceyloops.shared.ValueUtil;%0Aimport org.testng.annotations.Test;%0Aimport static org.testng.Assert.*;%0A%0Apublic class DisposingOnDisposeIntegrationTest%0A  extends AbstractArezIntegrationTest%0A%7B%0A  static final class Watcher%0A    implements Disposable%0A  %7B%0A    private final ComputedValue%3CBoolean%3E _condition;%0A    private final SafeProcedure _effect;%0A    private final Observer _observer;%0A%0A    Watcher( @Nonnull final SafeFunction%3CBoolean%3E condition, @Nonnull final SafeProcedure effect )%0A    %7B%0A      _effect = effect;%0A      final ArezContext context = Arez.context();%0A      _condition = context.computed( null, null, condition, null, this::dispose, null, null, Priority.NORMAL );%0A      final Procedure onDispose = () -%3E Disposable.dispose( _condition );%0A      _observer =%0A        context.autorun( null, null, true, this::checkCondition, Priority.NORMAL, false, true, true, onDispose );%0A      context.triggerScheduler();%0A    %7D%0A%0A    Observer getObserver()%0A    %7B%0A      return _observer;%0A    %7D%0A%0A    private void checkCondition()%0A    %7B%0A      if ( Disposable.isNotDisposed( _condition ) && _condition.get() )%0A      %7B%0A        Arez.context().safeAction( (String) null, true, true, _effect );%0A        Disposable.dispose( _observer );%0A      %7D%0A    %7D%0A%0A    @Override%0A    public void dispose()%0A    %7B%0A      Disposable.dispose( _observer );%0A    %7D%0A%0A    @Override%0A    public boolean isDisposed()%0A    %7B%0A      return Disposable.isDisposed( _observer );%0A    %7D%0A%0A    ComputedValue%3CBoolean%3E getCondition()%0A    %7B%0A      return _condition;%0A    %7D%0A  %7D%0A%0A  @Test%0A  public void dispose_releasesResources()%0A    throws Throwable%0A  %7B%0A    final ArezContext context = Arez.context();%0A%0A    final ObservableValue observable = context.observable();%0A%0A    final AtomicBoolean result = new AtomicBoolean();%0A%0A    final AtomicInteger conditionRun = new AtomicInteger();%0A    final AtomicInteger effectRun = new AtomicInteger();%0A%0A    final SafeFunction%3CBoolean%3E condition = () -%3E %7B%0A      conditionRun.incrementAndGet();%0A      observable.reportObserved();%0A      return result.get();%0A    %7D;%0A    final SafeProcedure procedure = effectRun::incrementAndGet;%0A%0A    final Watcher watcher = new Watcher( condition, procedure );%0A%0A    assertEquals( conditionRun.get(), 1 );%0A    assertEquals( effectRun.get(), 0 );%0A    assertEquals( Disposable.isDisposed( watcher ), false );%0A    assertEquals( Disposable.isDisposed( watcher.getObserver() ), false );%0A    assertEquals( Disposable.isDisposed( watcher.getCondition() ), false );%0A%0A    result.set( true );%0A    Disposable.dispose( watcher );%0A%0A    assertEquals( Disposable.isDisposed( watcher ), true );%0A    assertEquals( Disposable.isDisposed( watcher.getObserver() ), true );%0A    assertEquals( Disposable.isDisposed( watcher.getCondition() ), true );%0A%0A    context.action( ValueUtil.randomString(), true, observable::reportChanged );%0A%0A    assertEquals( conditionRun.get(), 1 );%0A    assertEquals( effectRun.get(), 0 );%0A  %7D%0A%7D%0A

Add integraiton test that picked up failure of onDispose hook not running within the scope of a transaction.