This is an automated email from the ASF dual-hosted git repository. klund pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/geode.git
The following commit(s) were added to refs/heads/develop by this push: new 5fd16a3 GEODE-6033: Support dynamic VMs in SharedErrorCollector (#3168) 5fd16a3 is described below commit 5fd16a3fc2806c2496ff505b8295d202c7eb8065 Author: Kirk Lund <kl...@apache.org> AuthorDate: Wed Feb 13 16:13:15 2019 -0800 GEODE-6033: Support dynamic VMs in SharedErrorCollector (#3168) Make SharedErrorCollector support create and bounce VM. --- .../tests/SharedErrorCollectorDistributedTest.java | 357 ++++++++++++++------- .../test/dunit/rules/SharedErrorCollector.java | 76 +++-- .../geode/test/junit/runners/TestRunner.java | 65 +++- 3 files changed, 351 insertions(+), 147 deletions(-) diff --git a/geode-dunit/src/distributedTest/java/org/apache/geode/test/dunit/rules/tests/SharedErrorCollectorDistributedTest.java b/geode-dunit/src/distributedTest/java/org/apache/geode/test/dunit/rules/tests/SharedErrorCollectorDistributedTest.java index 0887355..6e1d1df 100644 --- a/geode-dunit/src/distributedTest/java/org/apache/geode/test/dunit/rules/tests/SharedErrorCollectorDistributedTest.java +++ b/geode-dunit/src/distributedTest/java/org/apache/geode/test/dunit/rules/tests/SharedErrorCollectorDistributedTest.java @@ -14,186 +14,179 @@ */ package org.apache.geode.test.dunit.rules.tests; -import static org.apache.geode.test.dunit.VM.DEFAULT_VM_COUNT; import static org.apache.geode.test.dunit.VM.getAllVMs; import static org.apache.geode.test.dunit.VM.getVM; import static org.apache.geode.test.dunit.VM.getVMCount; +import static org.apache.geode.test.junit.runners.TestRunner.runTestWithExpectedFailures; +import static org.apache.geode.test.junit.runners.TestRunner.runTestWithValidation; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.is; import java.io.Serializable; import java.lang.reflect.Field; +import java.util.ArrayList; import java.util.List; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ErrorCollector; -import org.junit.runner.Result; -import org.junit.runner.notification.Failure; import org.apache.geode.test.dunit.VM; import org.apache.geode.test.dunit.rules.DistributedRule; import org.apache.geode.test.dunit.rules.SharedErrorCollector; -import org.apache.geode.test.junit.runners.TestRunner; +/** + * Distributed tests for {@link SharedErrorCollector}. + */ @SuppressWarnings("serial") public class SharedErrorCollectorDistributedTest { - static final String MESSAGE = "Failure message"; + private static final String MESSAGE = "Failure message"; @Rule public DistributedRule distributedRule = new DistributedRule(); - @Before - public void setUp() { - assertThat(getVMCount()).isGreaterThanOrEqualTo(DEFAULT_VM_COUNT); - } - @Test - public void errorCollectorHasExpectedField() throws Exception { + public void errorCollectorHasFieldNamedErrors() throws Exception { Field errorsField = ErrorCollector.class.getDeclaredField("errors"); + assertThat(errorsField.getDeclaringClass()).isEqualTo(ErrorCollector.class); } @Test - public void checkThatFailureInControllerIsReported() { - Result result = TestRunner.runTest(CheckThatFailsInController.class); + public void whenCheckThatFailsInController_resultIncludesError() { + assertFailsWithCollectedErrors(CheckThatFailsInController.class, new AssertionError(MESSAGE)); + } + + @Test + public void whenControllerAddsError_resultIncludesError() { + assertFailsWithCollectedErrors(AddErrorInController.class, new NullPointerException(MESSAGE)); + } - assertThat(result.wasSuccessful()).isFalse(); - List<Failure> failures = result.getFailures(); - assertThat(failures).hasSize(1); - assertThat(failures.get(0).getException()).isInstanceOf(AssertionError.class) - .hasMessageContaining(MESSAGE); + @Test + public void whenCheckThatFailsInDUnitVM_resultIncludesError() { + assertFailsWithCollectedErrors(CheckThatFailsInDUnitVM.class, new AssertionError(MESSAGE)); } @Test - public void addErrorInControllerIsReported() { - Result result = TestRunner.runTest(AddErrorInController.class); + public void whenCheckThatFailsInEveryDUnitVM_resultIncludesAllErrors() { + List<Throwable> errors = new ArrayList<>(); + for (VM vm : getAllVMs()) { + errors.add(new AssertionError(MESSAGE + " in VM-" + vm.getId())); + } - assertThat(result.wasSuccessful()).isFalse(); - List<Failure> failures = result.getFailures(); - assertThat(failures).hasSize(1); - assertThat(failures.get(0).getException()).isInstanceOf(NullPointerException.class) - .hasMessage(MESSAGE); + assertFailsWithCollectedErrors(CheckThatFailsInEveryDUnitVM.class, errors); } @Test - public void checkThatFailureInDUnitVMIsReported() { - Result result = TestRunner.runTest(CheckThatFailsInDUnitVM.class); + public void whenCheckThatFailsInEveryDUnitVMAndController_resultIncludesAllErrors() { + List<Throwable> errors = new ArrayList<>(); + errors.add(new AssertionError(MESSAGE + " in VM-CONTROLLER")); + for (VM vm : getAllVMs()) { + errors.add(new AssertionError(MESSAGE + " in VM-" + vm.getId())); + } + + assertFailsWithCollectedErrors(CheckThatFailsInEveryDUnitVMAndController.class, errors); + } - assertThat(result.wasSuccessful()).isFalse(); - List<Failure> failures = result.getFailures(); - assertThat(failures).hasSize(1); - assertThat(failures.get(0).getException()).isInstanceOf(AssertionError.class) - .hasMessageContaining(MESSAGE); + @Test + public void whenCheckThatFailsInMethodInDUnitVM_resultIncludesError() { + assertFailsWithCollectedErrors(CheckThatFailsInMethodInDUnitVM.class, + new AssertionError(MESSAGE)); } @Test - public void checkThatFailureInEveryDUnitVMIsReported() { - Result result = TestRunner.runTest(CheckThatFailsInEveryDUnitVM.class); - - assertThat(result.wasSuccessful()).isFalse(); - List<Failure> failures = result.getFailures(); - assertThat(failures).hasSize(getVMCount()); - int i = 0; - for (Failure failure : failures) { - assertThat(failure.getException()).isInstanceOf(AssertionError.class) - .hasMessageContaining(MESSAGE + " in VM-" + i++); + public void whenDUnitVMAddsError_resultIncludesError() { + assertFailsWithCollectedErrors(AddErrorInDUnitVM.class, new NullPointerException(MESSAGE)); + } + + @Test + public void whenEveryDUnitVMAddsError_resultIncludesAllErrors() { + List<Throwable> errors = new ArrayList<>(); + for (VM vm : getAllVMs()) { + errors.add(new NullPointerException(MESSAGE + " in VM-" + vm.getId())); } + + assertFailsWithCollectedErrors(AddErrorInEveryDUnitVM.class, errors); } @Test - public void checkThatFailureInEveryDUnitVMAndControllerIsReported() { - Result result = TestRunner.runTest(CheckThatFailsInEveryDUnitVMAndController.class); - - assertThat(result.wasSuccessful()).isFalse(); - List<Failure> failures = result.getFailures(); - assertThat(failures).hasSize(getVMCount() + 1); - boolean first = true; - int i = 0; - for (Failure failure : failures) { - if (first) { - assertThat(failure.getException()).isInstanceOf(AssertionError.class) - .hasMessageContaining(MESSAGE + " in VM-CONTROLLER"); - first = false; - } else { - assertThat(failure.getException()).isInstanceOf(AssertionError.class) - .hasMessageContaining(MESSAGE + " in VM-" + i++); - } + public void whenEveryDUnitVMAndControllerAddsError_resultIncludesAllErrors() { + List<Throwable> errors = new ArrayList<>(); + errors.add(new NullPointerException(MESSAGE + " in VM-CONTROLLER")); + for (VM vm : getAllVMs()) { + errors.add(new NullPointerException(MESSAGE + " in VM-" + vm.getId())); } + + assertFailsWithCollectedErrors(AddErrorInEveryDUnitVMAndController.class, errors); } @Test - public void checkThatFailureInMethodInDUnitVMIsReported() { - Result result = TestRunner.runTest(CheckThatFailsInMethodInDUnitVM.class); + public void whenDUnitVMAddsErrorInMethod_resultIncludesError() { + assertFailsWithCollectedErrors(AddErrorInMethodInDUnitVM.class, + new NullPointerException(MESSAGE)); + } - assertThat(result.wasSuccessful()).isFalse(); - List<Failure> failures = result.getFailures(); - assertThat(failures).hasSize(1); - assertThat(failures.get(0).getException()).isInstanceOf(AssertionError.class) - .hasMessageContaining(MESSAGE); + @Test + public void whenNewDUnitVMDoesNotAddError_resultIsSuccessful() { + assertPasses(AddDUnitVM.class); } @Test - public void addErrorInDUnitVMIsReported() { - Result result = TestRunner.runTest(AddErrorInDUnitVM.class); + public void whenNewDUnitVMAddsError_resultIncludesError() { + assertFailsWithCollectedErrors(AddErrorInNewDUnitVM.class, new NullPointerException(MESSAGE)); + } - assertThat(result.wasSuccessful()).isFalse(); - List<Failure> failures = result.getFailures(); - assertThat(failures).hasSize(1); - assertThat(failures.get(0).getException()).isInstanceOf(NullPointerException.class) - .hasMessage(MESSAGE); + @Test + public void whenBouncedDUnitVMDoesNotAddError_resultIsSuccessful() { + assertPasses(BounceDUnitVM.class); } @Test - public void addErrorInEveryDUnitVMIsReported() { - Result result = TestRunner.runTest(AddErrorInEveryDUnitVM.class); - - assertThat(result.wasSuccessful()).isFalse(); - List<Failure> failures = result.getFailures(); - assertThat(failures).hasSize(getVMCount()); - int i = 0; - for (Failure failure : failures) { - assertThat(failure.getException()).isInstanceOf(NullPointerException.class) - .hasMessageContaining(MESSAGE + " in VM-" + i++); - } + public void whenBouncedDUnitVMAddsErrorAfterBounce_resultIncludesError() { + assertFailsWithCollectedErrors(AddErrorInBouncedDUnitVM.class, + new NullPointerException(MESSAGE)); } @Test - public void addErrorInEveryDUnitVMAndControllerIsReported() { - Result result = TestRunner.runTest(AddErrorInEveryDUnitVMAndController.class); - - assertThat(result.wasSuccessful()).isFalse(); - List<Failure> failures = result.getFailures(); - assertThat(failures).hasSize(getVMCount() + 1); - boolean first = true; - int i = 0; - for (Failure failure : failures) { - if (first) { - assertThat(failure.getException()).isInstanceOf(NullPointerException.class) - .hasMessageContaining(MESSAGE + " in VM-CONTROLLER"); - first = false; - } else { - assertThat(failure.getException()).isInstanceOf(NullPointerException.class) - .hasMessageContaining(MESSAGE + " in VM-" + i++); - } - } + public void whenBouncedDUnitVMAddsErrorBeforeBounce_resultIncludesError() { + assertFailsWithCollectedErrors(AddErrorBeforeBouncingDUnitVM.class, + new NullPointerException(MESSAGE)); + } + + @Test + public void whenBouncedDUnitVMAddsErrorBeforeAndAfterBounce_resultIncludesError() { + assertFailsWithCollectedErrors(AddErrorBeforeAndAfterBouncingDUnitVM.class, + new NullPointerException(MESSAGE + "-before"), + new NullPointerException(MESSAGE + "-after")); + } + + @Test + public void whenCheckSucceedsDoesNotThrow_resultIsSuccessful() { + assertPasses(CheckSucceedsDoesNotThrow.class); } @Test - public void addErrorInMethodInDUnitVMIsReported() { - Result result = TestRunner.runTest(AddErrorInMethodInDUnitVM.class); + public void whenCheckSucceedsThrows_resultIncludesError() { + assertFailsWithCollectedErrors(CheckSucceedsThrows.class, new NullPointerException(MESSAGE)); + } + + private void assertPasses(final Class<?> test) { + runTestWithValidation(test); + } + + private void assertFailsWithCollectedErrors(final Class<?> test, + final Throwable... collectedErrors) { + runTestWithExpectedFailures(test, collectedErrors); + } - assertThat(result.wasSuccessful()).isFalse(); - List<Failure> failures = result.getFailures(); - assertThat(failures).hasSize(1); - assertThat(failures.get(0).getException()).isInstanceOf(NullPointerException.class) - .hasMessage(MESSAGE); + private void assertFailsWithCollectedErrors(final Class<?> test, + final List<Throwable> collectedErrors) { + runTestWithExpectedFailures(test, collectedErrors); } /** - * Used by test {@link #checkThatFailureInControllerIsReported()} + * Used by test {@link #whenCheckThatFailsInController_resultIncludesError()} */ public static class CheckThatFailsInController { @@ -207,7 +200,7 @@ public class SharedErrorCollectorDistributedTest { } /** - * Used by test {@link #addErrorInControllerIsReported()} + * Used by test {@link #whenControllerAddsError_resultIncludesError()} */ public static class AddErrorInController { @@ -221,7 +214,7 @@ public class SharedErrorCollectorDistributedTest { } /** - * Used by test {@link #checkThatFailureInDUnitVMIsReported()} + * Used by test {@link #whenCheckThatFailsInDUnitVM_resultIncludesError()} */ public static class CheckThatFailsInDUnitVM implements Serializable { @@ -235,7 +228,7 @@ public class SharedErrorCollectorDistributedTest { } /** - * Used by test {@link #checkThatFailureInEveryDUnitVMIsReported()} + * Used by test {@link #whenCheckThatFailsInEveryDUnitVM_resultIncludesAllErrors()} */ public static class CheckThatFailsInEveryDUnitVM implements Serializable { @@ -252,7 +245,7 @@ public class SharedErrorCollectorDistributedTest { } /** - * Used by test {@link #checkThatFailureInEveryDUnitVMAndControllerIsReported()} + * Used by test {@link #whenCheckThatFailsInEveryDUnitVMAndController_resultIncludesAllErrors()} */ public static class CheckThatFailsInEveryDUnitVMAndController implements Serializable { @@ -270,7 +263,7 @@ public class SharedErrorCollectorDistributedTest { } /** - * Used by test {@link #checkThatFailureInMethodInDUnitVMIsReported()} + * Used by test {@link #whenCheckThatFailsInMethodInDUnitVM_resultIncludesError()} */ public static class CheckThatFailsInMethodInDUnitVM implements Serializable { @@ -288,7 +281,7 @@ public class SharedErrorCollectorDistributedTest { } /** - * Used by test {@link #addErrorInDUnitVMIsReported()} + * Used by test {@link #whenDUnitVMAddsError_resultIncludesError()} */ public static class AddErrorInDUnitVM implements Serializable { @@ -302,7 +295,7 @@ public class SharedErrorCollectorDistributedTest { } /** - * Used by test {@link #addErrorInEveryDUnitVMIsReported()} + * Used by test {@link #whenEveryDUnitVMAddsError_resultIncludesAllErrors()} */ public static class AddErrorInEveryDUnitVM implements Serializable { @@ -319,7 +312,7 @@ public class SharedErrorCollectorDistributedTest { } /** - * Used by test {@link #addErrorInEveryDUnitVMAndControllerIsReported()} + * Used by test {@link #whenEveryDUnitVMAndControllerAddsError_resultIncludesAllErrors()} */ public static class AddErrorInEveryDUnitVMAndController implements Serializable { @@ -337,7 +330,7 @@ public class SharedErrorCollectorDistributedTest { } /** - * Used by test {@link #addErrorInMethodInDUnitVMIsReported()} + * Used by test {@link #whenDUnitVMAddsErrorInMethod_resultIncludesError()} */ public static class AddErrorInMethodInDUnitVM implements Serializable { @@ -353,4 +346,128 @@ public class SharedErrorCollectorDistributedTest { errorCollector.addError(new NullPointerException(MESSAGE)); } } + + /** + * Used by test {@link #whenNewDUnitVMDoesNotAddError_resultIsSuccessful()} + */ + public static class AddDUnitVM implements Serializable { + + @Rule + public SharedErrorCollector errorCollector = new SharedErrorCollector(); + + @Test + public void addDUnitVM() { + int startingVMCount = getVMCount(); + + getVM(getVMCount()); + + assertThat(getVMCount()).isGreaterThan(startingVMCount); + } + } + + /** + * Used by test {@link #whenNewDUnitVMAddsError_resultIncludesError()} + */ + public static class AddErrorInNewDUnitVM implements Serializable { + + @Rule + public SharedErrorCollector errorCollector = new SharedErrorCollector(); + + @Test + public void exceptionInNewDUnitVM() { + getVM(getVMCount()).invoke(() -> errorCollector.addError(new NullPointerException(MESSAGE))); + } + } + + /** + * Used by test {@link #whenBouncedDUnitVMDoesNotAddError_resultIsSuccessful()} + */ + public static class BounceDUnitVM implements Serializable { + + @Rule + public SharedErrorCollector errorCollector = new SharedErrorCollector(); + + @Test + public void addDUnitVM() { + getVM(0).bounce(); + } + } + + /** + * Used by test {@link #whenBouncedDUnitVMAddsErrorAfterBounce_resultIncludesError()} + */ + public static class AddErrorInBouncedDUnitVM implements Serializable { + + @Rule + public SharedErrorCollector errorCollector = new SharedErrorCollector(); + + @Test + public void exceptionInBouncedDUnitVM() { + getVM(0).bounce(); + getVM(0).invoke(() -> errorCollector.addError(new NullPointerException(MESSAGE))); + } + } + + /** + * Used by test {@link #whenBouncedDUnitVMAddsErrorBeforeBounce_resultIncludesError()} + */ + public static class AddErrorBeforeBouncingDUnitVM implements Serializable { + + @Rule + public SharedErrorCollector errorCollector = new SharedErrorCollector(); + + @Test + public void exceptionInBouncedDUnitVM() { + getVM(0).invoke(() -> errorCollector.addError(new NullPointerException(MESSAGE))); + getVM(0).bounce(); + } + } + + /** + * Used by test {@link #whenBouncedDUnitVMAddsErrorBeforeAndAfterBounce_resultIncludesError()} + */ + public static class AddErrorBeforeAndAfterBouncingDUnitVM implements Serializable { + + @Rule + public SharedErrorCollector errorCollector = new SharedErrorCollector(); + + @Test + public void exceptionInBouncedDUnitVM() { + getVM(0).invoke(() -> errorCollector.addError(new NullPointerException(MESSAGE + "-before"))); + getVM(0).bounce(); + getVM(0).invoke(() -> errorCollector.addError(new NullPointerException(MESSAGE + "-after"))); + } + } + + /** + * Used by test {@link #whenCheckSucceedsDoesNotThrow_resultIsSuccessful()} + */ + public static class CheckSucceedsDoesNotThrow implements Serializable { + + @Rule + public SharedErrorCollector errorCollector = new SharedErrorCollector(); + + @Test + public void checkSucceeds() { + Object result = errorCollector.checkSucceeds(() -> new Object()); + + assertThat(result).isNotNull(); + } + } + + /** + * Used by test {@link #whenCheckSucceedsThrows_resultIncludesError()} + */ + public static class CheckSucceedsThrows implements Serializable { + + @Rule + public SharedErrorCollector errorCollector = new SharedErrorCollector(); + + @Test + public void checkSucceeds() { + errorCollector.checkSucceeds(() -> { + throw new NullPointerException(MESSAGE); + }); + } + } } diff --git a/geode-dunit/src/main/java/org/apache/geode/test/dunit/rules/SharedErrorCollector.java b/geode-dunit/src/main/java/org/apache/geode/test/dunit/rules/SharedErrorCollector.java index 9afc08d..464885a 100644 --- a/geode-dunit/src/main/java/org/apache/geode/test/dunit/rules/SharedErrorCollector.java +++ b/geode-dunit/src/main/java/org/apache/geode/test/dunit/rules/SharedErrorCollector.java @@ -18,7 +18,10 @@ import static org.apache.geode.test.dunit.VM.getAllVMs; import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.Callable; import org.hamcrest.Matcher; @@ -53,10 +56,11 @@ import org.apache.geode.test.dunit.VM; * For a more thorough example, please see * {@code org.apache.geode.cache.ReplicateCacheListenerDistributedTest} in the tests of geode-core. */ -@SuppressWarnings("serial,unused") public class SharedErrorCollector extends AbstractDistributedRule { - private static volatile ProtectedErrorCollector errorCollector; + private static volatile VisibleErrorCollector errorCollector; + + private final Map<Integer, List<Throwable>> beforeBounceErrors = new HashMap<>(); public SharedErrorCollector() { // nothing @@ -64,16 +68,15 @@ public class SharedErrorCollector extends AbstractDistributedRule { @Override protected void before() { - invoker().invokeInEveryVMAndController(() -> errorCollector = new ProtectedErrorCollector()); + invoker().invokeInEveryVMAndController(() -> invokeBefore()); } @Override protected void after() throws Throwable { - ProtectedErrorCollector allErrors = errorCollector; + VisibleErrorCollector allErrors = errorCollector; try { for (VM vm : getAllVMs()) { - List<Throwable> remoteFailures = new ArrayList<>(); - remoteFailures.addAll(vm.invoke(() -> errorCollector.errors())); + List<Throwable> remoteFailures = new ArrayList<>(vm.invoke(() -> errorCollector.errors())); for (Throwable t : remoteFailures) { allErrors.addError(t); } @@ -84,6 +87,25 @@ public class SharedErrorCollector extends AbstractDistributedRule { } } + @Override + protected void afterCreateVM(VM vm) { + vm.invoke(() -> invokeBefore()); + } + + @Override + protected void beforeBounceVM(VM vm) { + beforeBounceErrors.put(vm.getId(), vm.invoke(() -> errorCollector.errors())); + } + + @Override + protected void afterBounceVM(VM vm) { + List<Throwable> beforeBounceErrorsForVM = beforeBounceErrors.remove(vm.getId()); + vm.invoke(() -> { + invokeBefore(); + errorCollector.addErrors(beforeBounceErrorsForVM); + }); + } + /** * @see ErrorCollector#addError(Throwable) */ @@ -112,31 +134,43 @@ public class SharedErrorCollector extends AbstractDistributedRule { return errorCollector.checkSucceeds(callable); } + private void invokeBefore() { + errorCollector = new VisibleErrorCollector(); + } + /** - * Uses reflection to acquire access to the {@code List} of {@code Throwable}s in - * {@link ErrorCollector}. + * Increases visibility of {@link #verify()} to public and uses reflection to acquire access to + * the {@code List} of {@code Throwable}s in {@link ErrorCollector}. */ - private static class ProtectedErrorCollector extends ErrorCollector { + private static class VisibleErrorCollector extends ErrorCollector { + + private final List<Throwable> visibleErrors; + + private VisibleErrorCollector() { + visibleErrors = getErrorsReference(); + } + + @Override + public void verify() throws Throwable { + super.verify(); + } - private final List<Throwable> protectedErrors; + List<Throwable> errors() { + return visibleErrors; + } + + void addErrors(Collection<Throwable> errors) { + visibleErrors.addAll(errors); + } - ProtectedErrorCollector() { + private List<Throwable> getErrorsReference() { try { Field superErrors = ErrorCollector.class.getDeclaredField("errors"); superErrors.setAccessible(true); - protectedErrors = (List<Throwable>) superErrors.get(this); + return (List<Throwable>) superErrors.get(this); } catch (IllegalAccessException | NoSuchFieldException e) { throw new RuntimeException(e); } } - - List<Throwable> errors() { - return protectedErrors; - } - - @Override - public void verify() throws Throwable { - super.verify(); - } } } diff --git a/geode-junit/src/main/java/org/apache/geode/test/junit/runners/TestRunner.java b/geode-junit/src/main/java/org/apache/geode/test/junit/runners/TestRunner.java index ca8a23f..0f3a875 100755 --- a/geode-junit/src/main/java/org/apache/geode/test/junit/runners/TestRunner.java +++ b/geode-junit/src/main/java/org/apache/geode/test/junit/runners/TestRunner.java @@ -14,10 +14,14 @@ */ package org.apache.geode.test.junit.runners; +import static java.util.Arrays.asList; +import static java.util.Objects.hash; import static org.assertj.core.api.Assertions.assertThat; import java.util.List; +import java.util.stream.Collectors; +import org.assertj.core.util.Streams; import org.junit.runner.JUnitCore; import org.junit.runner.Request; import org.junit.runner.Result; @@ -28,14 +32,16 @@ import org.junit.runner.notification.Failure; */ public class TestRunner { - protected TestRunner() {} + private TestRunner() { + // do not instantiate + } - public static Result runTest(final Class<?> test) { + public static Result runTest(Class<?> test) { JUnitCore junitCore = new JUnitCore(); return junitCore.run(Request.aClass(test).getRunner()); } - public static Result runTestWithValidation(final Class<?> test) { + public static Result runTestWithValidation(Class<?> test) { JUnitCore junitCore = new JUnitCore(); Result result = junitCore.run(Request.aClass(test).getRunner()); @@ -50,7 +56,7 @@ public class TestRunner { return result; } - public static Failure runTestWithExpectedFailure(final Class<?> test) { + public static Failure runTestWithExpectedFailure(Class<?> test) { JUnitCore junitCore = new JUnitCore(); Result result = junitCore.run(Request.aClass(test).getRunner()); @@ -60,13 +66,60 @@ public class TestRunner { return failures.get(0); } - public static List<Failure> runTestWithExpectedFailures(final Class<?> test) { + public static List<Failure> runTestWithExpectedFailures(Class<?> test, + Throwable... expectedThrowables) { + return runTestWithExpectedFailures(test, asList(expectedThrowables)); + } + + public static List<Failure> runTestWithExpectedFailures(Class<?> test, + List<Throwable> expectedThrowables) { + List<FailureInfo> expectedFailures = Streams.stream(expectedThrowables) + .map(f -> new FailureInfo(f.getClass(), f.getMessage())) + .collect(Collectors.toList()); + JUnitCore junitCore = new JUnitCore(); Result result = junitCore.run(Request.aClass(test).getRunner()); List<Failure> failures = result.getFailures(); - assertThat(failures).isNotEmpty(); + assertThat(failures).as("Actual failures").hasSameSizeAs(expectedFailures); + + List<FailureInfo> actualFailures = Streams.stream(failures) + .map(f -> new FailureInfo(f.getException().getClass(), f.getMessage())) + .collect(Collectors.toList()); + + assertThat(actualFailures).as("Actual failures info (Throwable and message)").hasSameElementsAs( + expectedFailures); return failures; } + + private static class FailureInfo { + + final Class<? extends Throwable> thrownClass; + final String expectedMessage; + + FailureInfo(Class<? extends Throwable> thrownClass, String expectedMessage) { + this.thrownClass = thrownClass; + this.expectedMessage = expectedMessage; + } + + @Override + public int hashCode() { + return hash(thrownClass, expectedMessage); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + FailureInfo that = (FailureInfo) obj; + return thrownClass.equals(that.thrownClass) && + (expectedMessage.contains(that.expectedMessage) || + that.expectedMessage.contains(expectedMessage)); + } + } }