package org.realityforge.arez.test;

import org.realityforge.arez.AbstractArezTest;
import org.realityforge.arez.ArezContext;
import org.realityforge.arez.TransactionMode;
import org.realityforge.guiceyloops.shared.ValueUtil;
import org.testng.annotations.Test;
import static org.testng.Assert.*;

/**
 * This class tests all the public methods of ArezContext
 * that should be visible outside package.
 */
public class ArezContextApiTest
  extends AbstractArezTest
{
  @Test
  public void transactionsCanProduceValues()
    throws Exception
  {
    final ArezContext context = new ArezContext();

    assertFalse( context.isTransactionActive() );

    final String expectedValue = ValueUtil.randomString();

    final String v0 =
      context.transaction( ValueUtil.randomString(), TransactionMode.READ_ONLY, null, () -> {
        assertTrue( context.isTransactionActive() );
        return expectedValue;
      } );

    assertFalse( context.isTransactionActive() );

    assertEquals( v0, expectedValue );
  }

  @Test
  public void transactionsCanBeNested()
    throws Exception
  {
    final ArezContext context = new ArezContext();

    assertFalse( context.isTransactionActive() );

    context.transaction( ValueUtil.randomString(), TransactionMode.READ_ONLY, null, () -> {
      assertTrue( context.isTransactionActive() );

      //First nested exception
      context.transaction( ValueUtil.randomString(), TransactionMode.READ_ONLY, null, () -> {
        assertTrue( context.isTransactionActive() );

        //Second nested exception
        context.transaction( ValueUtil.randomString(),
                             TransactionMode.READ_ONLY,
                             null,
                             () -> assertTrue( context.isTransactionActive() ) );

        assertTrue( context.isTransactionActive() );
      } );

      assertTrue( context.isTransactionActive() );
    } );

    assertFalse( context.isTransactionActive() );
  }

  @Test
  public void valueProducingTransactionsCanBeNested()
    throws Exception
  {
    final ArezContext context = new ArezContext();

    assertFalse( context.isTransactionActive() );

    final String expectedValue = ValueUtil.randomString();

    final String v0 =
      context.transaction( ValueUtil.randomString(), TransactionMode.READ_ONLY, null, () -> {
        assertTrue( context.isTransactionActive() );

        //First nested exception
        final String v1 =
          context.transaction( ValueUtil.randomString(), TransactionMode.READ_ONLY, null, () -> {
            assertTrue( context.isTransactionActive() );

            //Second nested exception
            final String v2 =
              context.transaction( ValueUtil.randomString(), TransactionMode.READ_ONLY, null, () -> {
                assertTrue( context.isTransactionActive() );
                return expectedValue;
              } );

            assertTrue( context.isTransactionActive() );

            return v2;
          } );

        assertTrue( context.isTransactionActive() );
        return v1;
      } );

    assertFalse( context.isTransactionActive() );

    assertEquals( v0, expectedValue );
  }
}

@@ -30,16 +30,66 @@
 .test;%0A%0A
+import java.util.concurrent.atomic.AtomicInteger;%0A
 import o
@@ -169,16 +169,106 @@
 ontext;%0A
+import org.realityforge.arez.ObserverErrorHandler;%0Aimport org.realityforge.arez.Reaction;%0A
 import o
@@ -601,16 +601,885 @@
 zTest%0A%7B%0A
+  @Test%0A  public void observerErrorHandler()%0A    throws Exception%0A  %7B%0A    final ArezContext context = new ArezContext();%0A%0A    final AtomicInteger callCount = new AtomicInteger();%0A%0A    final ObserverErrorHandler handler = ( observer, error, throwable ) -%3E callCount.incrementAndGet();%0A    context.addObserverErrorHandler( handler );%0A%0A    final Reaction reaction = o -%3E %7B%0A      throw new RuntimeException();%0A    %7D;%0A    // This will run immediately and generate an exception%0A    context.createObserver( ValueUtil.randomString(), TransactionMode.READ_ONLY, reaction, true );%0A%0A    assertEquals( callCount.get(), 1 );%0A%0A    context.removeObserverErrorHandler( handler );%0A%0A    // This will run immediately and generate an exception%0A    context.createObserver( ValueUtil.randomString(), TransactionMode.READ_ONLY, reaction, true );%0A%0A    assertEquals( callCount.get(), 1 );%0A  %7D%0A%0A
   @Test%0A

Add tests to public api of add/remove ObserverErrorHandler