This is an automated email from the ASF dual-hosted git repository.

domgarguilo pushed a commit to branch 2.1
in repository https://gitbox.apache.org/repos/asf/accumulo.git


The following commit(s) were added to refs/heads/2.1 by this push:
     new ab4bafb1ea Refactor common code into Before/AfterEach blocks in FateIT 
(#5741)
ab4bafb1ea is described below

commit ab4bafb1eab54d768fc0b2c98f70965875ba6c3d
Author: Dom G. <[email protected]>
AuthorDate: Thu Jul 17 10:22:27 2025 -0400

    Refactor common code into Before/AfterEach blocks in FateIT (#5741)
    
    * Refactor common code into Before/AfterEach blocks in FateIT
    * Return early in Fate.shutdown() when the ExecutorService is null
---
 .../java/org/apache/accumulo/core/fate/Fate.java   |   7 +-
 .../accumulo/test/fate/zookeeper/FateIT.java       | 411 +++++++++------------
 2 files changed, 178 insertions(+), 240 deletions(-)

diff --git a/core/src/main/java/org/apache/accumulo/core/fate/Fate.java 
b/core/src/main/java/org/apache/accumulo/core/fate/Fate.java
index f1c3bb9e64..c6f30dd4ab 100644
--- a/core/src/main/java/org/apache/accumulo/core/fate/Fate.java
+++ b/core/src/main/java/org/apache/accumulo/core/fate/Fate.java
@@ -421,8 +421,11 @@ public class Fate<T> {
    */
   public void shutdown(boolean wait) {
     keepRunning.set(false);
+    if (executor == null) {
+      return;
+    }
+    executor.shutdownNow();
     if (wait) {
-      executor.shutdownNow();
       while (!executor.isTerminated()) {
         try {
           executor.awaitTermination(1, SECONDS);
@@ -430,8 +433,6 @@ public class Fate<T> {
           throw new IllegalStateException(e);
         }
       }
-    } else {
-      executor.shutdown();
     }
   }
 }
diff --git 
a/test/src/main/java/org/apache/accumulo/test/fate/zookeeper/FateIT.java 
b/test/src/main/java/org/apache/accumulo/test/fate/zookeeper/FateIT.java
index 155df5ab9c..4480709873 100644
--- a/test/src/main/java/org/apache/accumulo/test/fate/zookeeper/FateIT.java
+++ b/test/src/main/java/org/apache/accumulo/test/fate/zookeeper/FateIT.java
@@ -68,7 +68,9 @@ import org.apache.accumulo.test.util.Wait;
 import org.apache.accumulo.test.zookeeper.ZooKeeperTestingServer;
 import org.apache.zookeeper.KeeperException;
 import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Tag;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
@@ -192,6 +194,8 @@ public class FateIT {
 
   private static AtomicInteger nextFateDir = new AtomicInteger(0);
 
+  Fate<Manager> fate;
+
   private enum ExceptionLocation {
     CALL, IS_READY
   };
@@ -208,6 +212,28 @@ public class FateIT {
     zc = new ZooCache(zk, null);
   }
 
+  @BeforeEach
+  public void beforeEach() throws Exception {
+    final ZooStore<Manager> zooStore = new ZooStore<>(ZK_ROOT + 
Constants.ZFATE, zk, zc);
+    final AgeOffStore<Manager> store = new AgeOffStore<>(zooStore, 3000, 
System::currentTimeMillis);
+
+    Manager manager = createMock(Manager.class);
+    ServerContext sctx = createMock(ServerContext.class);
+    expect(manager.getContext()).andReturn(sctx).anyTimes();
+    expect(sctx.getZooKeeperRoot()).andReturn(ZK_ROOT).anyTimes();
+    expect(sctx.getZooReaderWriter()).andReturn(zk).anyTimes();
+    replay(manager, sctx);
+
+    fate = new Fate<>(manager, store, TraceRepo::toLogString);
+  }
+
+  @AfterEach
+  public void afterEach() {
+    if (fate != null) {
+      fate.shutdown(true);
+    }
+  }
+
   @AfterAll
   public static void teardown() throws Exception {
     szk.close();
@@ -217,135 +243,92 @@ public class FateIT {
   @Timeout(30)
   public void testTransactionStatus() throws Exception {
 
-    final ZooStore<Manager> zooStore = new ZooStore<Manager>(ZK_ROOT + 
Constants.ZFATE, zk, zc);
-    final AgeOffStore<Manager> store =
-        new AgeOffStore<Manager>(zooStore, 3000, System::currentTimeMillis);
+    ConfigurationCopy config = new ConfigurationCopy();
+    config.set(Property.GENERAL_THREADPOOL_SIZE, "2");
+    config.set(Property.MANAGER_FATE_THREADPOOL_SIZE, "1");
+
+    callStarted = new CountDownLatch(1);
+    finishCall = new CountDownLatch(1);
 
-    Manager manager = createMock(Manager.class);
-    ServerContext sctx = createMock(ServerContext.class);
-    expect(manager.getContext()).andReturn(sctx).anyTimes();
-    expect(sctx.getZooKeeperRoot()).andReturn(ZK_ROOT).anyTimes();
-    expect(sctx.getZooReaderWriter()).andReturn(zk).anyTimes();
-    replay(manager, sctx);
+    long txid = fate.startTransaction();
+    assertEquals(TStatus.NEW, getTxStatus(zk, txid));
+    fate.seedTransaction("TestOperation", txid, new TestOperation(NS, TID), 
true, "Test Op");
+    assertEquals(TStatus.SUBMITTED, getTxStatus(zk, txid));
 
-    Fate<Manager> fate = new Fate<Manager>(manager, store, 
TraceRepo::toLogString);
-    try {
-      ConfigurationCopy config = new ConfigurationCopy();
-      config.set(Property.GENERAL_THREADPOOL_SIZE, "2");
-      config.set(Property.MANAGER_FATE_THREADPOOL_SIZE, "1");
-
-      callStarted = new CountDownLatch(1);
-      finishCall = new CountDownLatch(1);
-
-      long txid = fate.startTransaction();
-      assertEquals(TStatus.NEW, getTxStatus(zk, txid));
-      fate.seedTransaction("TestOperation", txid, new TestOperation(NS, TID), 
true, "Test Op");
-      assertEquals(TStatus.SUBMITTED, getTxStatus(zk, txid));
-
-      fate.startTransactionRunners(config, new ScheduledThreadPoolExecutor(2));
-      // Wait for the transaction runner to be scheduled.
-      UtilWaitThread.sleep(3000);
-
-      // wait for call() to be called
-      callStarted.await();
-      assertEquals(IN_PROGRESS, getTxStatus(zk, txid));
-      // tell the op to exit the method
-      finishCall.countDown();
-      // Check that it transitions to SUCCESSFUL and gets removed
-      final var sawSuccess = new AtomicBoolean(false);
-      Wait.waitFor(() -> {
-        TStatus s;
-        try {
-          switch (s = getTxStatus(zk, txid)) {
-            case IN_PROGRESS:
-              if (sawSuccess.get()) {
-                fail("Should never see IN_PROGRESS after seeing SUCCESSFUL");
-              }
-              break;
-            case SUCCESSFUL:
-              // expected, but might be too quick to be detected
-              if (sawSuccess.compareAndSet(false, true)) {
-                LOG.debug("Saw expected transaction status change to 
SUCCESSFUL");
-              }
-              break;
-            default:
-              fail("Saw unexpected status: " + s);
-          }
-        } catch (KeeperException e) {
-          if (e.code() == KeeperException.Code.NONODE) {
-            if (!sawSuccess.get()) {
-              LOG.debug("Never saw transaction status change to SUCCESSFUL, 
but that's okay");
+    fate.startTransactionRunners(config, new ScheduledThreadPoolExecutor(2));
+    // Wait for the transaction runner to be scheduled.
+    UtilWaitThread.sleep(3000);
+
+    // wait for call() to be called
+    callStarted.await();
+    assertEquals(IN_PROGRESS, getTxStatus(zk, txid));
+    // tell the op to exit the method
+    finishCall.countDown();
+    // Check that it transitions to SUCCESSFUL and gets removed
+    final var sawSuccess = new AtomicBoolean(false);
+    Wait.waitFor(() -> {
+      TStatus s;
+      try {
+        switch (s = getTxStatus(zk, txid)) {
+          case IN_PROGRESS:
+            if (sawSuccess.get()) {
+              fail("Should never see IN_PROGRESS after seeing SUCCESSFUL");
+            }
+            break;
+          case SUCCESSFUL:
+            // expected, but might be too quick to be detected
+            if (sawSuccess.compareAndSet(false, true)) {
+              LOG.debug("Saw expected transaction status change to 
SUCCESSFUL");
             }
-            return true;
-          } else {
-            fail("Unexpected error thrown: " + e.getMessage());
+            break;
+          default:
+            fail("Saw unexpected status: " + s);
+        }
+      } catch (KeeperException e) {
+        if (e.code() == KeeperException.Code.NONODE) {
+          if (!sawSuccess.get()) {
+            LOG.debug("Never saw transaction status change to SUCCESSFUL, but 
that's okay");
           }
+          return true;
+        } else {
+          fail("Unexpected error thrown: " + e.getMessage());
         }
-        // keep waiting for NoNode
-        return false;
-      }, SECONDS.toMillis(30), 10);
-    } finally {
-      fate.shutdown(true);
-    }
+      }
+      // keep waiting for NoNode
+      return false;
+    }, SECONDS.toMillis(30), 10);
   }
 
   @Test
   public void testCancelWhileNew() throws Exception {
-    final ZooStore<Manager> zooStore = new ZooStore<Manager>(ZK_ROOT + 
Constants.ZFATE, zk, zc);
-    final AgeOffStore<Manager> store =
-        new AgeOffStore<Manager>(zooStore, 3000, System::currentTimeMillis);
 
-    Manager manager = createMock(Manager.class);
-    ServerContext sctx = createMock(ServerContext.class);
-    expect(manager.getContext()).andReturn(sctx).anyTimes();
-    expect(sctx.getZooKeeperRoot()).andReturn(ZK_ROOT).anyTimes();
-    expect(sctx.getZooReaderWriter()).andReturn(zk).anyTimes();
-    replay(manager, sctx);
+    ConfigurationCopy config = new ConfigurationCopy();
+    config.set(Property.GENERAL_THREADPOOL_SIZE, "2");
+    config.set(Property.MANAGER_FATE_THREADPOOL_SIZE, "1");
+    fate.startTransactionRunners(config, new ScheduledThreadPoolExecutor(2));
 
-    Fate<Manager> fate = new Fate<Manager>(manager, store, 
TraceRepo::toLogString);
-    try {
-      ConfigurationCopy config = new ConfigurationCopy();
-      config.set(Property.GENERAL_THREADPOOL_SIZE, "2");
-      config.set(Property.MANAGER_FATE_THREADPOOL_SIZE, "1");
-      fate.startTransactionRunners(config, new ScheduledThreadPoolExecutor(2));
-
-      // Wait for the transaction runner to be scheduled.
-      UtilWaitThread.sleep(3000);
-
-      callStarted = new CountDownLatch(1);
-      finishCall = new CountDownLatch(1);
-
-      long txid = fate.startTransaction();
-      LOG.debug("Starting test testCancelWhileNew with {}", 
FateTxId.formatTid(txid));
-      assertEquals(NEW, getTxStatus(zk, txid));
-      // cancel the transaction
-      assertTrue(fate.cancel(txid));
-      assertTrue(FAILED_IN_PROGRESS == getTxStatus(zk, txid) || FAILED == 
getTxStatus(zk, txid));
-      fate.seedTransaction("TestOperation", txid, new TestOperation(NS, TID), 
true, "Test Op");
-      Wait.waitFor(() -> FAILED == getTxStatus(zk, txid));
-      // nothing should have run
-      assertEquals(1, callStarted.getCount());
-      fate.delete(txid);
-      assertThrows(KeeperException.NoNodeException.class, () -> 
getTxStatus(zk, txid));
-    } finally {
-      fate.shutdown(true);
-    }
+    // Wait for the transaction runner to be scheduled.
+    UtilWaitThread.sleep(3000);
+
+    callStarted = new CountDownLatch(1);
+    finishCall = new CountDownLatch(1);
+
+    long txid = fate.startTransaction();
+    LOG.debug("Starting test testCancelWhileNew with {}", 
FateTxId.formatTid(txid));
+    assertEquals(NEW, getTxStatus(zk, txid));
+    // cancel the transaction
+    assertTrue(fate.cancel(txid));
+    assertTrue(FAILED_IN_PROGRESS == getTxStatus(zk, txid) || FAILED == 
getTxStatus(zk, txid));
+    fate.seedTransaction("TestOperation", txid, new TestOperation(NS, TID), 
true, "Test Op");
+    Wait.waitFor(() -> FAILED == getTxStatus(zk, txid));
+    // nothing should have run
+    assertEquals(1, callStarted.getCount());
+    fate.delete(txid);
+    assertThrows(KeeperException.NoNodeException.class, () -> getTxStatus(zk, 
txid));
   }
 
   @Test
   public void testCancelWhileSubmittedNotRunning() throws Exception {
-    final ZooStore<Manager> zooStore = new ZooStore<Manager>(ZK_ROOT + 
Constants.ZFATE, zk, zc);
-    final AgeOffStore<Manager> store =
-        new AgeOffStore<Manager>(zooStore, 3000, System::currentTimeMillis);
-
-    Manager manager = createMock(Manager.class);
-    ServerContext sctx = createMock(ServerContext.class);
-    expect(manager.getContext()).andReturn(sctx).anyTimes();
-    expect(sctx.getZooKeeperRoot()).andReturn(ZK_ROOT).anyTimes();
-    expect(sctx.getZooReaderWriter()).andReturn(zk).anyTimes();
-    replay(manager, sctx);
-
-    Fate<Manager> fate = new Fate<Manager>(manager, store, 
TraceRepo::toLogString);
     ConfigurationCopy config = new ConfigurationCopy();
     config.set(Property.GENERAL_THREADPOOL_SIZE, "2");
 
@@ -367,88 +350,57 @@ public class FateIT {
 
   @Test
   public void testCancelWhileSubmittedAndRunning() throws Exception {
-    final ZooStore<Manager> zooStore = new ZooStore<Manager>(ZK_ROOT + 
Constants.ZFATE, zk, zc);
-    final AgeOffStore<Manager> store =
-        new AgeOffStore<Manager>(zooStore, 3000, System::currentTimeMillis);
 
-    Manager manager = createMock(Manager.class);
-    ServerContext sctx = createMock(ServerContext.class);
-    expect(manager.getContext()).andReturn(sctx).anyTimes();
-    expect(sctx.getZooKeeperRoot()).andReturn(ZK_ROOT).anyTimes();
-    expect(sctx.getZooReaderWriter()).andReturn(zk).anyTimes();
-    replay(manager, sctx);
+    ConfigurationCopy config = new ConfigurationCopy();
+    config.set(Property.GENERAL_THREADPOOL_SIZE, "2");
+    config.set(Property.MANAGER_FATE_THREADPOOL_SIZE, "1");
+    fate.startTransactionRunners(config, new ScheduledThreadPoolExecutor(2));
 
-    Fate<Manager> fate = new Fate<Manager>(manager, store, 
TraceRepo::toLogString);
-    try {
-      ConfigurationCopy config = new ConfigurationCopy();
-      config.set(Property.GENERAL_THREADPOOL_SIZE, "2");
-      config.set(Property.MANAGER_FATE_THREADPOOL_SIZE, "1");
-      fate.startTransactionRunners(config, new ScheduledThreadPoolExecutor(2));
-
-      // Wait for the transaction runner to be scheduled.
-      UtilWaitThread.sleep(3000);
-
-      callStarted = new CountDownLatch(1);
-      finishCall = new CountDownLatch(1);
-
-      long txid = fate.startTransaction();
-      LOG.debug("Starting test testCancelWhileSubmitted with {}", 
FateTxId.formatTid(txid));
-      assertEquals(NEW, getTxStatus(zk, txid));
-      fate.seedTransaction("TestOperation", txid, new TestOperation(NS, TID), 
false, "Test Op");
-      Wait.waitFor(() -> IN_PROGRESS == getTxStatus(zk, txid));
-      // This is false because the transaction runner has reserved the FaTe
-      // transaction.
-      assertFalse(fate.cancel(txid));
-      callStarted.await();
-      finishCall.countDown();
-      Wait.waitFor(() -> IN_PROGRESS != getTxStatus(zk, txid));
-      fate.delete(txid);
-      assertThrows(KeeperException.NoNodeException.class, () -> 
getTxStatus(zk, txid));
-    } finally {
-      fate.shutdown(true);
-    }
+    // Wait for the transaction runner to be scheduled.
+    UtilWaitThread.sleep(3000);
+
+    callStarted = new CountDownLatch(1);
+    finishCall = new CountDownLatch(1);
+
+    long txid = fate.startTransaction();
+    LOG.debug("Starting test testCancelWhileSubmitted with {}", 
FateTxId.formatTid(txid));
+    assertEquals(NEW, getTxStatus(zk, txid));
+    fate.seedTransaction("TestOperation", txid, new TestOperation(NS, TID), 
false, "Test Op");
+    Wait.waitFor(() -> IN_PROGRESS == getTxStatus(zk, txid));
+    // This is false because the transaction runner has reserved the FaTe
+    // transaction.
+    assertFalse(fate.cancel(txid));
+    callStarted.await();
+    finishCall.countDown();
+    Wait.waitFor(() -> IN_PROGRESS != getTxStatus(zk, txid));
+    fate.delete(txid);
+    assertThrows(KeeperException.NoNodeException.class, () -> getTxStatus(zk, 
txid));
   }
 
   @Test
   public void testCancelWhileInCall() throws Exception {
-    final ZooStore<Manager> zooStore = new ZooStore<Manager>(ZK_ROOT + 
Constants.ZFATE, zk, zc);
-    final AgeOffStore<Manager> store =
-        new AgeOffStore<Manager>(zooStore, 3000, System::currentTimeMillis);
 
-    Manager manager = createMock(Manager.class);
-    ServerContext sctx = createMock(ServerContext.class);
-    expect(manager.getContext()).andReturn(sctx).anyTimes();
-    expect(sctx.getZooKeeperRoot()).andReturn(ZK_ROOT).anyTimes();
-    expect(sctx.getZooReaderWriter()).andReturn(zk).anyTimes();
-    replay(manager, sctx);
+    ConfigurationCopy config = new ConfigurationCopy();
+    config.set(Property.GENERAL_THREADPOOL_SIZE, "2");
+    config.set(Property.MANAGER_FATE_THREADPOOL_SIZE, "1");
 
-    Fate<Manager> fate = new Fate<Manager>(manager, store, 
TraceRepo::toLogString);
-    try {
-      ConfigurationCopy config = new ConfigurationCopy();
-      config.set(Property.GENERAL_THREADPOOL_SIZE, "2");
-      config.set(Property.MANAGER_FATE_THREADPOOL_SIZE, "1");
-
-      callStarted = new CountDownLatch(1);
-      finishCall = new CountDownLatch(1);
-
-      long txid = fate.startTransaction();
-      LOG.debug("Starting test testCancelWhileInCall with {}", 
FateTxId.formatTid(txid));
-      assertEquals(NEW, getTxStatus(zk, txid));
-      fate.seedTransaction("TestOperation", txid, new TestOperation(NS, TID), 
true, "Test Op");
-      assertEquals(SUBMITTED, getTxStatus(zk, txid));
-
-      fate.startTransactionRunners(config, new ScheduledThreadPoolExecutor(2));
-      // Wait for the transaction runner to be scheduled.
-      UtilWaitThread.sleep(3000);
-
-      // wait for call() to be called
-      callStarted.await();
-      // cancel the transaction
-      assertFalse(fate.cancel(txid));
-    } finally {
-      fate.shutdown(true);
-    }
+    callStarted = new CountDownLatch(1);
+    finishCall = new CountDownLatch(1);
+
+    long txid = fate.startTransaction();
+    LOG.debug("Starting test testCancelWhileInCall with {}", 
FateTxId.formatTid(txid));
+    assertEquals(NEW, getTxStatus(zk, txid));
+    fate.seedTransaction("TestOperation", txid, new TestOperation(NS, TID), 
true, "Test Op");
+    assertEquals(SUBMITTED, getTxStatus(zk, txid));
 
+    fate.startTransactionRunners(config, new ScheduledThreadPoolExecutor(2));
+    // Wait for the transaction runner to be scheduled.
+    UtilWaitThread.sleep(3000);
+
+    // wait for call() to be called
+    callStarted.await();
+    // cancel the transaction
+    assertFalse(fate.cancel(txid));
   }
 
   @Test
@@ -460,58 +412,43 @@ public class FateIT {
      * is called and throws an exception (in call() or isReady()). It is then 
expected that: 1)
      * undo() is called on Repo3, 2) undo() is called on Repo2, 3) undo() is 
called on Repo1
      */
-    final ZooStore<Manager> zooStore = new ZooStore<Manager>(ZK_ROOT + 
Constants.ZFATE, zk, zc);
-    final AgeOffStore<Manager> store =
-        new AgeOffStore<Manager>(zooStore, 3000, System::currentTimeMillis);
 
-    Manager manager = createMock(Manager.class);
-    ServerContext sctx = createMock(ServerContext.class);
-    expect(manager.getContext()).andReturn(sctx).anyTimes();
-    expect(sctx.getZooKeeperRoot()).andReturn(ZK_ROOT).anyTimes();
-    expect(sctx.getZooReaderWriter()).andReturn(zk).anyTimes();
-    replay(manager, sctx);
+    ConfigurationCopy config = new ConfigurationCopy();
+    config.set(Property.GENERAL_THREADPOOL_SIZE, "2");
+    config.set(Property.MANAGER_FATE_THREADPOOL_SIZE, "1");
+    fate.startTransactionRunners(config, new ScheduledThreadPoolExecutor(2));
 
-    Fate<Manager> fate = new Fate<Manager>(manager, store, 
TraceRepo::toLogString);
-    try {
-      ConfigurationCopy config = new ConfigurationCopy();
-      config.set(Property.GENERAL_THREADPOOL_SIZE, "2");
-      config.set(Property.MANAGER_FATE_THREADPOOL_SIZE, "1");
-      fate.startTransactionRunners(config, new ScheduledThreadPoolExecutor(2));
-
-      // Wait for the transaction runner to be scheduled.
-      UtilWaitThread.sleep(3000);
-
-      List<String> expectedUndoOrder = List.of("OP3", "OP2", "OP1");
-      /*
-       * Test exception in call()
-       */
-      undoLatch = new CountDownLatch(TestOperationFails.TOTAL_NUM_OPS);
-      long txid = fate.startTransaction();
-      assertEquals(NEW, getTxStatus(zk, txid));
-      fate.seedTransaction("TestOperationFails", txid,
-          new TestOperationFails(1, ExceptionLocation.CALL), false, "Test Op 
Fails");
-      // Wait for all the undo() calls to complete
-      undoLatch.await();
-      assertEquals(expectedUndoOrder, TestOperationFails.undoOrder);
-      assertEquals(FAILED, fate.waitForCompletion(txid));
-      assertTrue(fate.getException(txid).getMessage().contains("call() 
failed"));
-      /*
-       * Test exception in isReady()
-       */
-      TestOperationFails.undoOrder = new ArrayList<>();
-      undoLatch = new CountDownLatch(TestOperationFails.TOTAL_NUM_OPS);
-      txid = fate.startTransaction();
-      assertEquals(NEW, getTxStatus(zk, txid));
-      fate.seedTransaction("TestOperationFails", txid,
-          new TestOperationFails(1, ExceptionLocation.IS_READY), false, "Test 
Op Fails");
-      // Wait for all the undo() calls to complete
-      undoLatch.await();
-      assertEquals(expectedUndoOrder, TestOperationFails.undoOrder);
-      assertEquals(FAILED, fate.waitForCompletion(txid));
-      assertTrue(fate.getException(txid).getMessage().contains("isReady() 
failed"));
-    } finally {
-      fate.shutdown(true);
-    }
+    // Wait for the transaction runner to be scheduled.
+    UtilWaitThread.sleep(3000);
+
+    List<String> expectedUndoOrder = List.of("OP3", "OP2", "OP1");
+    /*
+     * Test exception in call()
+     */
+    undoLatch = new CountDownLatch(TestOperationFails.TOTAL_NUM_OPS);
+    long txid = fate.startTransaction();
+    assertEquals(NEW, getTxStatus(zk, txid));
+    fate.seedTransaction("TestOperationFails", txid,
+        new TestOperationFails(1, ExceptionLocation.CALL), false, "Test Op 
Fails");
+    // Wait for all the undo() calls to complete
+    undoLatch.await();
+    assertEquals(expectedUndoOrder, TestOperationFails.undoOrder);
+    assertEquals(FAILED, fate.waitForCompletion(txid));
+    assertTrue(fate.getException(txid).getMessage().contains("call() failed"));
+    /*
+     * Test exception in isReady()
+     */
+    TestOperationFails.undoOrder = new ArrayList<>();
+    undoLatch = new CountDownLatch(TestOperationFails.TOTAL_NUM_OPS);
+    txid = fate.startTransaction();
+    assertEquals(NEW, getTxStatus(zk, txid));
+    fate.seedTransaction("TestOperationFails", txid,
+        new TestOperationFails(1, ExceptionLocation.IS_READY), false, "Test Op 
Fails");
+    // Wait for all the undo() calls to complete
+    undoLatch.await();
+    assertEquals(expectedUndoOrder, TestOperationFails.undoOrder);
+    assertEquals(FAILED, fate.waitForCompletion(txid));
+    assertTrue(fate.getException(txid).getMessage().contains("isReady() 
failed"));
   }
 
   private static void inCall() throws InterruptedException {

Reply via email to