From 82f68500c0dfedd98e0769888a46eff7899c5ab7 Mon Sep 17 00:00:00 2001 From: Dave Borowitz Date: Mon, 17 Jul 2017 11:49:36 -0400 Subject: [PATCH] Improve BatchRefUpdateTest readability * Factor out helpers for setting up and executing updates. * Use common assert methods, with a special enum type that papers over the fact that there is no ReceiveCommand.Result for transaction aborted. * Static import ReceiveCommand.Type constants. * Add blank lines to separate repo setup, update execution, and asserts. Change-Id: Ic3717f94331abfc7ae3e92065f3fe32026bf7cea --- .../storage/file/BatchRefUpdateTest.java | 421 +++++++++--------- 1 file changed, 216 insertions(+), 205 deletions(-) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java index 3cfc82d66..06c47acf2 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java @@ -43,22 +43,35 @@ package org.eclipse.jgit.internal.storage.file; +import static org.eclipse.jgit.internal.storage.file.BatchRefUpdateTest.Result.LOCK_FAILURE; +import static org.eclipse.jgit.internal.storage.file.BatchRefUpdateTest.Result.OK; +import static org.eclipse.jgit.internal.storage.file.BatchRefUpdateTest.Result.REJECTED_MISSING_OBJECT; +import static org.eclipse.jgit.internal.storage.file.BatchRefUpdateTest.Result.REJECTED_NONFASTFORWARD; +import static org.eclipse.jgit.internal.storage.file.BatchRefUpdateTest.Result.TRANSACTION_ABORTED; import static org.eclipse.jgit.lib.ObjectId.zeroId; +import static org.eclipse.jgit.transport.ReceiveCommand.Type.CREATE; +import static org.eclipse.jgit.transport.ReceiveCommand.Type.DELETE; +import static org.eclipse.jgit.transport.ReceiveCommand.Type.UPDATE; +import static org.eclipse.jgit.transport.ReceiveCommand.Type.UPDATE_NONFASTFORWARD; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.Collection; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.function.Predicate; import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase; import org.eclipse.jgit.junit.StrictWorkMonitor; import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.BatchRefUpdate; +import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Ref; @@ -103,43 +116,26 @@ public class BatchRefUpdateTest extends LocalDiskRepositoryTestCase { B = repo.commit(repo.getRevWalk().parseCommit(A)); } - private BatchRefUpdate newBatchUpdate() { - BatchRefUpdate u = refdir.newBatchUpdate(); - if (atomic) { - assertTrue(u.isAtomic()); - } else { - u.setAtomic(false); - } - return u; - } - @Test public void simpleNoForce() throws IOException { writeLooseRef("refs/heads/master", A); writeLooseRef("refs/heads/masters", B); - List commands = Arrays.asList( - new ReceiveCommand(A, B, "refs/heads/master", - ReceiveCommand.Type.UPDATE), - new ReceiveCommand(B, A, "refs/heads/masters", - ReceiveCommand.Type.UPDATE_NONFASTFORWARD)); - BatchRefUpdate batchUpdate = newBatchUpdate(); - batchUpdate.addCommand(commands); - batchUpdate.execute(new RevWalk(diskRepo), new StrictWorkMonitor()); - Map refs = refdir.getRefs(RefDatabase.ALL); - assertEquals(ReceiveCommand.Result.REJECTED_NONFASTFORWARD, commands - .get(1).getResult()); + + List cmds = Arrays.asList( + new ReceiveCommand(A, B, "refs/heads/master", UPDATE), + new ReceiveCommand(B, A, "refs/heads/masters", UPDATE_NONFASTFORWARD)); + execute(newBatchUpdate(cmds)); + if (atomic) { - assertTrue(ReceiveCommand.isTransactionAborted(commands.get(0))); - assertEquals("[HEAD, refs/heads/master, refs/heads/masters]", refs - .keySet().toString()); - assertEquals(A.getId(), refs.get("refs/heads/master").getObjectId()); - assertEquals(B.getId(), refs.get("refs/heads/masters").getObjectId()); + assertResults(cmds, TRANSACTION_ABORTED, REJECTED_NONFASTFORWARD); + assertRefs( + "refs/heads/master", A, + "refs/heads/masters", B); } else { - assertEquals(ReceiveCommand.Result.OK, commands.get(0).getResult()); - assertEquals("[HEAD, refs/heads/master, refs/heads/masters]", refs - .keySet().toString()); - assertEquals(B.getId(), refs.get("refs/heads/master").getObjectId()); - assertEquals(B.getId(), refs.get("refs/heads/masters").getObjectId()); + assertResults(cmds, OK, REJECTED_NONFASTFORWARD); + assertRefs( + "refs/heads/master", B, + "refs/heads/masters", B); } } @@ -147,85 +143,65 @@ public class BatchRefUpdateTest extends LocalDiskRepositoryTestCase { public void simpleForce() throws IOException { writeLooseRef("refs/heads/master", A); writeLooseRef("refs/heads/masters", B); - List commands = Arrays.asList( - new ReceiveCommand(A, B, "refs/heads/master", - ReceiveCommand.Type.UPDATE), - new ReceiveCommand(B, A, "refs/heads/masters", - ReceiveCommand.Type.UPDATE_NONFASTFORWARD)); - BatchRefUpdate batchUpdate = newBatchUpdate(); - batchUpdate.setAllowNonFastForwards(true); - batchUpdate.addCommand(commands); - batchUpdate.execute(new RevWalk(diskRepo), new StrictWorkMonitor()); - Map refs = refdir.getRefs(RefDatabase.ALL); - assertEquals(ReceiveCommand.Result.OK, commands.get(0).getResult()); - assertEquals(ReceiveCommand.Result.OK, commands.get(1).getResult()); - assertEquals("[HEAD, refs/heads/master, refs/heads/masters]", refs - .keySet().toString()); - assertEquals(B.getId(), refs.get("refs/heads/master").getObjectId()); - assertEquals(A.getId(), refs.get("refs/heads/masters").getObjectId()); + + List cmds = Arrays.asList( + new ReceiveCommand(A, B, "refs/heads/master", UPDATE), + new ReceiveCommand(B, A, "refs/heads/masters", UPDATE_NONFASTFORWARD)); + execute(newBatchUpdate(cmds).setAllowNonFastForwards(true)); + + assertResults(cmds, OK, OK); + assertRefs( + "refs/heads/master", B, + "refs/heads/masters", A); } @Test public void nonFastForwardDoesNotDoExpensiveMergeCheck() throws IOException { writeLooseRef("refs/heads/master", B); - List commands = Arrays.asList( - new ReceiveCommand(B, A, "refs/heads/master", - ReceiveCommand.Type.UPDATE_NONFASTFORWARD)); - BatchRefUpdate batchUpdate = newBatchUpdate(); - batchUpdate.setAllowNonFastForwards(true); - batchUpdate.addCommand(commands); - batchUpdate.execute(new RevWalk(diskRepo) { - @Override - public boolean isMergedInto(RevCommit base, RevCommit tip) { - throw new AssertionError("isMergedInto() should not be called"); - } - }, new StrictWorkMonitor()); - Map refs = refdir.getRefs(RefDatabase.ALL); - assertEquals(ReceiveCommand.Result.OK, commands.get(0).getResult()); - assertEquals(A.getId(), refs.get("refs/heads/master").getObjectId()); + + List cmds = Arrays.asList( + new ReceiveCommand(B, A, "refs/heads/master", UPDATE_NONFASTFORWARD)); + try (RevWalk rw = new RevWalk(diskRepo) { + @Override + public boolean isMergedInto(RevCommit base, RevCommit tip) { + throw new AssertionError("isMergedInto() should not be called"); + } + }) { + newBatchUpdate(cmds) + .setAllowNonFastForwards(true) + .execute(rw, new StrictWorkMonitor()); + } + + assertResults(cmds, OK); + assertRefs("refs/heads/master", A); } @Test public void fileDirectoryConflict() throws IOException { writeLooseRef("refs/heads/master", A); writeLooseRef("refs/heads/masters", B); - List commands = Arrays.asList( - new ReceiveCommand(A, B, "refs/heads/master", - ReceiveCommand.Type.UPDATE), - new ReceiveCommand(zeroId(), A, "refs/heads/master/x", - ReceiveCommand.Type.CREATE), - new ReceiveCommand(zeroId(), A, "refs/heads", - ReceiveCommand.Type.CREATE)); - BatchRefUpdate batchUpdate = newBatchUpdate(); - batchUpdate.setAllowNonFastForwards(true); - batchUpdate.addCommand(commands); - batchUpdate - .execute(new RevWalk(diskRepo), NullProgressMonitor.INSTANCE); - Map refs = refdir.getRefs(RefDatabase.ALL); + + List cmds = Arrays.asList( + new ReceiveCommand(A, B, "refs/heads/master", UPDATE), + new ReceiveCommand(zeroId(), A, "refs/heads/master/x", CREATE), + new ReceiveCommand(zeroId(), A, "refs/heads", CREATE)); + execute(newBatchUpdate(cmds).setAllowNonFastForwards(true), false); if (atomic) { // Atomic update sees that master and master/x are conflicting, then marks // the first one in the list as LOCK_FAILURE and aborts the rest. - assertEquals(ReceiveCommand.Result.LOCK_FAILURE, - commands.get(0).getResult()); - assertTrue(ReceiveCommand.isTransactionAborted(commands.get(1))); - assertTrue(ReceiveCommand.isTransactionAborted(commands.get(2))); - assertEquals("[HEAD, refs/heads/master, refs/heads/masters]", refs - .keySet().toString()); - assertEquals(A.getId(), refs.get("refs/heads/master").getObjectId()); - assertEquals(B.getId(), refs.get("refs/heads/masters").getObjectId()); + assertResults(cmds, + LOCK_FAILURE, TRANSACTION_ABORTED, TRANSACTION_ABORTED); + assertRefs( + "refs/heads/master", A, + "refs/heads/masters", B); } else { // Non-atomic updates are applied in order: master succeeds, then master/x // fails due to conflict. - assertEquals(ReceiveCommand.Result.OK, commands.get(0).getResult()); - assertEquals(ReceiveCommand.Result.LOCK_FAILURE, commands.get(1) - .getResult()); - assertEquals(ReceiveCommand.Result.LOCK_FAILURE, commands.get(2) - .getResult()); - assertEquals("[HEAD, refs/heads/master, refs/heads/masters]", refs - .keySet().toString()); - assertEquals(B.getId(), refs.get("refs/heads/master").getObjectId()); - assertEquals(B.getId(), refs.get("refs/heads/masters").getObjectId()); + assertResults(cmds, OK, LOCK_FAILURE, LOCK_FAILURE); + assertRefs( + "refs/heads/master", B, + "refs/heads/masters", B); } } @@ -233,170 +209,205 @@ public class BatchRefUpdateTest extends LocalDiskRepositoryTestCase { public void conflictThanksToDelete() throws IOException { writeLooseRef("refs/heads/master", A); writeLooseRef("refs/heads/masters", B); - List commands = Arrays.asList( - new ReceiveCommand(A, B, "refs/heads/master", - ReceiveCommand.Type.UPDATE), - new ReceiveCommand(zeroId(), A, "refs/heads/masters/x", - ReceiveCommand.Type.CREATE), - new ReceiveCommand(B, zeroId(), "refs/heads/masters", - ReceiveCommand.Type.DELETE)); - BatchRefUpdate batchUpdate = newBatchUpdate(); - batchUpdate.setAllowNonFastForwards(true); - batchUpdate.addCommand(commands); - batchUpdate.execute(new RevWalk(diskRepo), new StrictWorkMonitor()); - Map refs = refdir.getRefs(RefDatabase.ALL); - assertEquals(ReceiveCommand.Result.OK, commands.get(0).getResult()); - assertEquals(ReceiveCommand.Result.OK, commands.get(1).getResult()); - assertEquals(ReceiveCommand.Result.OK, commands.get(2).getResult()); - assertEquals("[HEAD, refs/heads/master, refs/heads/masters/x]", refs - .keySet().toString()); - assertEquals(A.getId(), refs.get("refs/heads/masters/x").getObjectId()); + + List cmds = Arrays.asList( + new ReceiveCommand(A, B, "refs/heads/master", UPDATE), + new ReceiveCommand(zeroId(), A, "refs/heads/masters/x", CREATE), + new ReceiveCommand(B, zeroId(), "refs/heads/masters", DELETE)); + execute(newBatchUpdate(cmds).setAllowNonFastForwards(true)); + + assertResults(cmds, OK, OK, OK); + assertRefs( + "refs/heads/master", B, + "refs/heads/masters/x", A); } @Test public void updateToMissingObject() throws IOException { writeLooseRef("refs/heads/master", A); + ObjectId bad = ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); - List commands = Arrays.asList( - new ReceiveCommand(A, bad, "refs/heads/master", - ReceiveCommand.Type.UPDATE), - new ReceiveCommand(zeroId(), B, "refs/heads/foo2", - ReceiveCommand.Type.CREATE)); - BatchRefUpdate batchUpdate = newBatchUpdate(); - batchUpdate.setAllowNonFastForwards(true); - batchUpdate.addCommand(commands); - batchUpdate.execute(new RevWalk(diskRepo), NullProgressMonitor.INSTANCE); - Map refs = refdir.getRefs(RefDatabase.ALL); - assertEquals(ReceiveCommand.Result.REJECTED_MISSING_OBJECT, - commands.get(0).getResult()); + List cmds = Arrays.asList( + new ReceiveCommand(A, bad, "refs/heads/master", UPDATE), + new ReceiveCommand(zeroId(), B, "refs/heads/foo2", CREATE)); + execute(newBatchUpdate(cmds).setAllowNonFastForwards(true), false); if (atomic) { - assertTrue(ReceiveCommand.isTransactionAborted(commands.get(1))); - assertEquals("[HEAD, refs/heads/master]", refs.keySet() - .toString()); - assertEquals(A.getId(), refs.get("refs/heads/master").getObjectId()); + assertResults(cmds, REJECTED_MISSING_OBJECT, TRANSACTION_ABORTED); + assertRefs("refs/heads/master", A); } else { - assertEquals(ReceiveCommand.Result.OK, commands.get(1).getResult()); - assertEquals("[HEAD, refs/heads/foo2, refs/heads/master]", refs.keySet() - .toString()); - assertEquals(A.getId(), refs.get("refs/heads/master").getObjectId()); - assertEquals(B.getId(), refs.get("refs/heads/foo2").getObjectId()); + assertResults(cmds, REJECTED_MISSING_OBJECT, OK); + assertRefs( + "refs/heads/master", A, + "refs/heads/foo2", B); } } @Test public void addMissingObject() throws IOException { writeLooseRef("refs/heads/master", A); + ObjectId bad = ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); - List commands = Arrays.asList( - new ReceiveCommand(A, B, "refs/heads/master", - ReceiveCommand.Type.UPDATE), - new ReceiveCommand(zeroId(), bad, "refs/heads/foo2", - ReceiveCommand.Type.CREATE)); - BatchRefUpdate batchUpdate = newBatchUpdate(); - batchUpdate.setAllowNonFastForwards(true); - batchUpdate.addCommand(commands); - batchUpdate.execute(new RevWalk(diskRepo), NullProgressMonitor.INSTANCE); - Map refs = refdir.getRefs(RefDatabase.ALL); - assertEquals(ReceiveCommand.Result.REJECTED_MISSING_OBJECT, - commands.get(1).getResult()); + List cmds = Arrays.asList( + new ReceiveCommand(A, B, "refs/heads/master", UPDATE), + new ReceiveCommand(zeroId(), bad, "refs/heads/foo2", CREATE)); + execute(newBatchUpdate(cmds).setAllowNonFastForwards(true), false); if (atomic) { - assertTrue(ReceiveCommand.isTransactionAborted(commands.get(0))); - assertEquals("[HEAD, refs/heads/master]", refs.keySet().toString()); - assertEquals(A.getId(), refs.get("refs/heads/master").getObjectId()); + assertResults(cmds, TRANSACTION_ABORTED, REJECTED_MISSING_OBJECT); + assertRefs("refs/heads/master", A); } else { - assertEquals(ReceiveCommand.Result.OK, commands.get(0).getResult()); - assertEquals("[HEAD, refs/heads/master]", refs.keySet() - .toString()); - assertEquals(B.getId(), refs.get("refs/heads/master").getObjectId()); + assertResults(cmds, OK, REJECTED_MISSING_OBJECT); + assertRefs("refs/heads/master", B); } } @Test public void oneNonExistentRef() throws IOException { - List commands = Arrays.asList( - new ReceiveCommand(A, B, "refs/heads/foo1", - ReceiveCommand.Type.UPDATE), - new ReceiveCommand(zeroId(), B, "refs/heads/foo2", - ReceiveCommand.Type.CREATE)); - BatchRefUpdate batchUpdate = newBatchUpdate(); - batchUpdate.setAllowNonFastForwards(true); - batchUpdate.addCommand(commands); - batchUpdate.execute(new RevWalk(diskRepo), new StrictWorkMonitor()); - Map refs = refdir.getRefs(RefDatabase.ALL); - assertEquals(ReceiveCommand.Result.LOCK_FAILURE, - commands.get(0).getResult()); + List cmds = Arrays.asList( + new ReceiveCommand(A, B, "refs/heads/foo1", UPDATE), + new ReceiveCommand(zeroId(), B, "refs/heads/foo2", CREATE)); + execute(newBatchUpdate(cmds).setAllowNonFastForwards(true)); if (atomic) { - assertTrue(ReceiveCommand.isTransactionAborted(commands.get(1))); - assertEquals("[]", refs.keySet().toString()); + assertResults(cmds, LOCK_FAILURE, TRANSACTION_ABORTED); + assertRefs(); } else { - assertEquals(ReceiveCommand.Result.OK, commands.get(1).getResult()); - assertEquals("[refs/heads/foo2]", refs.keySet().toString()); - assertEquals(B.getId(), refs.get("refs/heads/foo2").getObjectId()); + assertResults(cmds, LOCK_FAILURE, OK); + assertRefs("refs/heads/foo2", B); } } @Test public void oneRefWrongOldValue() throws IOException { writeLooseRef("refs/heads/master", A); - List commands = Arrays.asList( - new ReceiveCommand(B, B, "refs/heads/master", - ReceiveCommand.Type.UPDATE), - new ReceiveCommand(zeroId(), B, "refs/heads/foo2", - ReceiveCommand.Type.CREATE)); - BatchRefUpdate batchUpdate = newBatchUpdate(); - batchUpdate.setAllowNonFastForwards(true); - batchUpdate.addCommand(commands); - batchUpdate.execute(new RevWalk(diskRepo), new StrictWorkMonitor()); - Map refs = refdir.getRefs(RefDatabase.ALL); - assertEquals(ReceiveCommand.Result.LOCK_FAILURE, - commands.get(0).getResult()); + + List cmds = Arrays.asList( + new ReceiveCommand(B, B, "refs/heads/master", UPDATE), + new ReceiveCommand(zeroId(), B, "refs/heads/foo2", CREATE)); + execute(newBatchUpdate(cmds).setAllowNonFastForwards(true)); if (atomic) { - assertTrue(ReceiveCommand.isTransactionAborted(commands.get(1))); - assertEquals("[HEAD, refs/heads/master]", refs.keySet().toString()); - assertEquals(A.getId(), refs.get("refs/heads/master").getObjectId()); + assertResults(cmds, LOCK_FAILURE, TRANSACTION_ABORTED); + assertRefs("refs/heads/master", A); } else { - assertEquals(ReceiveCommand.Result.OK, commands.get(1).getResult()); - assertEquals("[HEAD, refs/heads/foo2, refs/heads/master]", refs - .keySet().toString()); - assertEquals(A.getId(), refs.get("refs/heads/master").getObjectId()); - assertEquals(B.getId(), refs.get("refs/heads/foo2").getObjectId()); + assertResults(cmds, LOCK_FAILURE, OK); + assertRefs( + "refs/heads/master", A, + "refs/heads/foo2", B); } } @Test public void nonExistentRef() throws IOException { writeLooseRef("refs/heads/master", A); - List commands = Arrays.asList( - new ReceiveCommand(A, B, "refs/heads/master", - ReceiveCommand.Type.UPDATE), - new ReceiveCommand(A, zeroId(), "refs/heads/foo2", - ReceiveCommand.Type.DELETE)); - BatchRefUpdate batchUpdate = newBatchUpdate(); - batchUpdate.setAllowNonFastForwards(true); - batchUpdate.addCommand(commands); - batchUpdate.execute(new RevWalk(diskRepo), new StrictWorkMonitor()); - Map refs = refdir.getRefs(RefDatabase.ALL); - assertEquals(ReceiveCommand.Result.LOCK_FAILURE, - commands.get(1).getResult()); + + List cmds = Arrays.asList( + new ReceiveCommand(A, B, "refs/heads/master", UPDATE), + new ReceiveCommand(A, zeroId(), "refs/heads/foo2", DELETE)); + execute(newBatchUpdate(cmds).setAllowNonFastForwards(true)); if (atomic) { - assertTrue(ReceiveCommand.isTransactionAborted(commands.get(0))); - assertEquals("[HEAD, refs/heads/master]", refs.keySet().toString()); - assertEquals(A.getId(), refs.get("refs/heads/master").getObjectId()); + assertResults(cmds, TRANSACTION_ABORTED, LOCK_FAILURE); + assertRefs("refs/heads/master", A); } else { - assertEquals(ReceiveCommand.Result.OK, commands.get(0).getResult()); - assertEquals("[HEAD, refs/heads/master]", refs.keySet().toString()); - assertEquals(B.getId(), refs.get("refs/heads/master").getObjectId()); + assertResults(cmds, OK, LOCK_FAILURE); + assertRefs("refs/heads/master", B); } } private void writeLooseRef(String name, AnyObjectId id) throws IOException { write(new File(diskRepo.getDirectory(), name), id.name() + "\n"); } + + private BatchRefUpdate newBatchUpdate(List cmds) { + BatchRefUpdate u = refdir.newBatchUpdate(); + if (atomic) { + assertTrue(u.isAtomic()); + } else { + u.setAtomic(false); + } + u.addCommand(cmds); + return u; + } + + private void execute(BatchRefUpdate u) throws IOException { + execute(u, false); + } + + private void execute(BatchRefUpdate u, boolean strictWork) throws IOException { + try (RevWalk rw = new RevWalk(diskRepo)) { + u.execute(rw, + strictWork ? new StrictWorkMonitor() : NullProgressMonitor.INSTANCE); + } + } + + private void assertRefs(Object... args) throws IOException { + if (args.length % 2 != 0) { + throw new IllegalArgumentException( + "expected even number of args: " + Arrays.toString(args)); + } + + Map expected = new LinkedHashMap<>(); + for (int i = 0; i < args.length; i += 2) { + expected.put((String) args[i], (AnyObjectId) args[i + 1]); + } + + Map refs = refdir.getRefs(RefDatabase.ALL); + Ref actualHead = refs.remove(Constants.HEAD); + if (actualHead != null) { + String actualLeafName = actualHead.getLeaf().getName(); + assertEquals( + "expected HEAD to point to refs/heads/master, got: " + actualLeafName, + "refs/heads/master", actualLeafName); + AnyObjectId expectedMaster = expected.get("refs/heads/master"); + assertNotNull("expected master ref since HEAD exists", expectedMaster); + assertEquals(expectedMaster, actualHead.getObjectId()); + } + + Map actual = new LinkedHashMap<>(); + refs.forEach((n, r) -> actual.put(n, r.getObjectId())); + + assertEquals(expected.keySet(), actual.keySet()); + actual.forEach((n, a) -> assertEquals(n, expected.get(n), a)); + } + + enum Result { + OK(ReceiveCommand.Result.OK), + LOCK_FAILURE(ReceiveCommand.Result.LOCK_FAILURE), + REJECTED_NONFASTFORWARD(ReceiveCommand.Result.REJECTED_NONFASTFORWARD), + REJECTED_MISSING_OBJECT(ReceiveCommand.Result.REJECTED_MISSING_OBJECT), + TRANSACTION_ABORTED(ReceiveCommand::isTransactionAborted); + + final Predicate p; + + private Result(Predicate p) { + this.p = p; + } + + private Result(ReceiveCommand.Result result) { + this(c -> c.getResult() == result); + } + } + + private void assertResults( + List cmds, Result... expected) { + if (expected.length != cmds.size()) { + throw new IllegalArgumentException( + "expected " + cmds.size() + " result args"); + } + for (int i = 0; i < cmds.size(); i++) { + ReceiveCommand c = cmds.get(i); + Result r = expected[i]; + assertTrue( + String.format( + "result of command (%d) should be %s: %s %s%s", + Integer.valueOf(i), r, c, + c.getResult(), + c.getMessage() != null ? " (" + c.getMessage() + ")" : ""), + r.p.test(c)); + } + } }