summaryrefslogtreecommitdiffstats
path: root/vespajlib/src/test/java/com/yahoo/transaction/NestedTransactionTestCase.java
diff options
context:
space:
mode:
Diffstat (limited to 'vespajlib/src/test/java/com/yahoo/transaction/NestedTransactionTestCase.java')
-rw-r--r--vespajlib/src/test/java/com/yahoo/transaction/NestedTransactionTestCase.java181
1 files changed, 181 insertions, 0 deletions
diff --git a/vespajlib/src/test/java/com/yahoo/transaction/NestedTransactionTestCase.java b/vespajlib/src/test/java/com/yahoo/transaction/NestedTransactionTestCase.java
new file mode 100644
index 00000000000..d75daa506b4
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/transaction/NestedTransactionTestCase.java
@@ -0,0 +1,181 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.transaction;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author bratseth
+ */
+public class NestedTransactionTestCase {
+
+ @Test
+ public void testNestedTransaction() {
+ NestedTransaction t = new NestedTransaction();
+ t.add(new TransactionTypeB("B1"), TransactionTypeC.class);
+ t.add(new TransactionTypeC("C1"));
+ t.add(new TransactionTypeA("A1"), TransactionTypeB.class);
+ t.add(new TransactionTypeA("A2"));
+ t.add(new TransactionTypeC("C2"));
+
+ // Add two tasks to run after commit
+ MutableInteger tasksRun = new MutableInteger();
+ t.onCommitted(() -> { tasksRun.value++; });
+ t.onCommitted(() -> { tasksRun.value++; });
+
+ assertEquals(3, t.transactions().size());
+ assertEquals(TransactionTypeA.class, t.transactions().get(0).getClass());
+ assertEquals(TransactionTypeB.class, t.transactions().get(1).getClass());
+ assertEquals(TransactionTypeC.class, t.transactions().get(2).getClass());
+
+ assertEquals("A1", ((MockOperation)t.transactions().get(0).operations().get(0)).name);
+ assertEquals("A2", ((MockOperation)t.transactions().get(0).operations().get(1)).name);
+ assertEquals("B1", ((MockOperation)t.transactions().get(1).operations().get(0)).name);
+ assertEquals("C1", ((MockOperation)t.transactions().get(2).operations().get(0)).name);
+ assertEquals("C2", ((MockOperation)t.transactions().get(2).operations().get(1)).name);
+
+ t.commit();
+ assertTrue(((MockTransaction)t.transactions().get(0)).committed);
+ assertTrue(((MockTransaction)t.transactions().get(1)).committed);
+ assertTrue(((MockTransaction)t.transactions().get(2)).committed);
+ assertEquals("After commit tasks are run", 2, tasksRun.value);
+ }
+
+ @Test
+ public void testNestedTransactionFailingOnCommit() {
+ NestedTransaction t = new NestedTransaction();
+ t.add(new TransactionTypeC("C1"));
+ t.add(new TransactionTypeA("A1"), TransactionTypeB.class, FailAtCommitTransactionType.class);
+ t.add(new TransactionTypeA("A2"));
+ t.add(new FailAtCommitTransactionType("Fail"), TransactionTypeC.class);
+ t.add(new TransactionTypeC("C2"));
+ t.add(new TransactionTypeB("B1"), TransactionTypeC.class, FailAtCommitTransactionType.class);
+
+ // Add task to run after commit
+ MutableInteger tasksRun = new MutableInteger();
+ t.onCommitted(() -> {
+ tasksRun.value++;
+ });
+
+ assertEquals(4, t.transactions().size());
+ assertEquals(TransactionTypeA.class, t.transactions().get(0).getClass());
+ assertEquals(TransactionTypeB.class, t.transactions().get(1).getClass());
+ assertEquals(FailAtCommitTransactionType.class, t.transactions().get(2).getClass());
+ assertEquals(TransactionTypeC.class, t.transactions().get(3).getClass());
+
+ try { t.commit(); } catch (IllegalStateException expected) { }
+ assertTrue(((MockTransaction)t.transactions().get(0)).rolledback);
+ assertTrue(((MockTransaction)t.transactions().get(1)).rolledback);
+ assertFalse(((MockTransaction) t.transactions().get(2)).committed);
+ assertEquals("After commit tasks are not run", 0, tasksRun.value);
+ }
+
+ @Test
+ public void testConflictingOrdering() {
+ NestedTransaction t = new NestedTransaction();
+ t.add(new TransactionTypeA("A1"), TransactionTypeB.class);
+ t.add(new TransactionTypeB("B1"), TransactionTypeC.class);
+ t.add(new TransactionTypeC("C1"), TransactionTypeA.class);
+ try {
+ t.commit();
+ fail("Expected exception");
+ }
+ catch (IllegalStateException expected) {
+ }
+ }
+
+ private static class TransactionTypeA extends MockTransaction {
+ public TransactionTypeA(String name) { super(name); }
+ }
+
+ private static class TransactionTypeB extends MockTransaction {
+ public TransactionTypeB(String name) { super(name); }
+ }
+
+ private static class TransactionTypeC extends MockTransaction {
+ public TransactionTypeC(String name) { super(name); }
+ }
+
+ private static class FailAtCommitTransactionType extends MockTransaction {
+ public FailAtCommitTransactionType(String name) { super(name); }
+ @Override
+ public void commit() {
+ throw new RuntimeException();
+ }
+ }
+
+ private static class MockTransaction implements Transaction {
+
+ public boolean prepared = false, committed = false, rolledback = false;
+ private List<Operation> operations = new ArrayList<>();
+
+ public MockTransaction(String name) {
+ operations.add(new MockOperation(name));
+ }
+
+ @Override
+ public Transaction add(Operation operation) {
+ operations.add(operation);
+ return this;
+ }
+
+ @Override
+ public Transaction add(List<Operation> operation) {
+ operations.addAll(operation);
+ return this;
+ }
+
+ @Override
+ public List<Operation> operations() {
+ return operations;
+ }
+
+ @Override
+ public void prepare() {
+ prepared = true;
+ }
+
+ @Override
+ public void commit() {
+ if ( ! prepared)
+ throw new IllegalStateException("Commit before prepare");
+ committed = true;
+ }
+
+ @Override
+ public void rollbackOrLog() {
+ if ( ! committed)
+ throw new IllegalStateException("Rollback before commit");
+ rolledback = true;
+ }
+
+ @Override
+ public void close() {
+ }
+
+ }
+
+ private static class MockOperation implements Transaction.Operation {
+
+ public String name;
+
+ public MockOperation(String name) {
+ this.name = name;
+ }
+
+ }
+
+ private static class MutableInteger {
+
+ public int value = 0;
+
+ }
+
+}