diff --git a/plugin/src/main/java/org/gradle/testretry/internal/config/TestTaskConfigurer.java b/plugin/src/main/java/org/gradle/testretry/internal/config/TestTaskConfigurer.java index a09530ac..00287b83 100644 --- a/plugin/src/main/java/org/gradle/testretry/internal/config/TestTaskConfigurer.java +++ b/plugin/src/main/java/org/gradle/testretry/internal/config/TestTaskConfigurer.java @@ -27,12 +27,15 @@ import org.gradle.api.tasks.testing.Test; import org.gradle.internal.reflect.Instantiator; import org.gradle.testretry.TestRetryTaskExtension; +import org.gradle.testretry.internal.executer.LastResultHolder; import org.gradle.testretry.internal.executer.RetryTestExecuter; +import org.gradle.testretry.internal.executer.RoundResult; import org.gradle.util.GradleVersion; import org.jetbrains.annotations.NotNull; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.stream.Collectors; public final class TestTaskConfigurer { @@ -60,8 +63,10 @@ public static void configureTestTask(Test test, ObjectFactory objectFactory, Pro test.getExtensions().add(TestRetryTaskExtension.class, TestRetryTaskExtension.NAME, extension); - test.doFirst(new ConditionalTaskAction(isDeactivatedByTestDistributionPlugin, new InitTaskAction(adapter, objectFactory))); - test.doLast(new ConditionalTaskAction(isDeactivatedByTestDistributionPlugin, new FinalizeTaskAction())); + LastResultHolder lastResultHolder = new LastResultHolder(); + + test.doFirst(new ConditionalTaskAction(isDeactivatedByTestDistributionPlugin, new InitTaskAction(adapter, objectFactory, lastResultHolder))); + test.doLast(new ConditionalTaskAction(isDeactivatedByTestDistributionPlugin, new FinalizeTaskAction(adapter, lastResultHolder))); } private static void ensureThatNoRetryExtensionIsPresent(Test testTask) { @@ -127,10 +132,10 @@ private static boolean callShouldTestRetryPluginBeDeactivated(Test test) { } } - private static RetryTestExecuter createRetryTestExecuter(Test task, TestRetryTaskExtensionAdapter extension, ObjectFactory objectFactory) { + private static RetryTestExecuter createRetryTestExecuter(Test task, TestRetryTaskExtensionAdapter extension, ObjectFactory objectFactory, LastResultHolder lastResultHolder) { TestExecuter delegate = getTestExecuter(task); Instantiator instantiator = invoke(declaredMethod(AbstractTestTask.class, "getInstantiator"), task); - return new RetryTestExecuter(task, extension, delegate, instantiator, objectFactory, task.getTestClassesDirs().getFiles(), task.getClasspath().getFiles()); + return new RetryTestExecuter(task, extension, delegate, instantiator, objectFactory, task.getTestClassesDirs().getFiles(), task.getClasspath().getFiles(), lastResultHolder); } private static TestExecuter getTestExecuter(Test task) { @@ -161,32 +166,49 @@ public void execute(@NotNull Task task) { } } - private static class FinalizeTaskAction implements Action { + public static class FinalizeTaskAction implements Action { + + private final TestRetryTaskExtensionAccessor extension; + private final LastResultHolder lastResultHolder; + + public FinalizeTaskAction(TestRetryTaskExtensionAccessor extension, LastResultHolder lastResultHolder) { + this.extension = extension; + this.lastResultHolder = lastResultHolder; + } @Override public void execute(@NotNull Test task) { - TestExecuter testExecuter = getTestExecuter(task); - if (testExecuter instanceof RetryTestExecuter) { - ((RetryTestExecuter) testExecuter).failWithNonRetriedTestsIfAny(); - } else { - throw new IllegalStateException("Unexpected test executer: " + testExecuter); + RoundResult lastResult = lastResultHolder.get(); + boolean hasNonRetriedTests = lastResult != null && !lastResult.nonRetriedTests.isEmpty(); + if (extension.getSimulateNotRetryableTest() || hasNonRetriedTests) { + throw new IllegalStateException("org.gradle.test-retry was unable to retry the following test methods, which is unexpected. Please file a bug report at https://github.com/gradle/test-retry-gradle-plugin/issues" + + ( + lastResult == null + ? "" + : lastResult.nonRetriedTests.stream() + .flatMap(entry -> entry.getValue().stream().map(methodName -> " " + entry.getKey() + "#" + methodName)) + .collect(Collectors.joining("\n", "\n", "\n")) + ) + ); } } } - private static class InitTaskAction implements Action { + public static class InitTaskAction implements Action { private final TestRetryTaskExtensionAdapter adapter; private final ObjectFactory objectFactory; + private final LastResultHolder lastResultHolder; - public InitTaskAction(TestRetryTaskExtensionAdapter adapter, ObjectFactory objectFactory) { + public InitTaskAction(TestRetryTaskExtensionAdapter adapter, ObjectFactory objectFactory, LastResultHolder lastResultHolder) { this.adapter = adapter; this.objectFactory = objectFactory; + this.lastResultHolder = lastResultHolder; } @Override public void execute(@NotNull Test task) { - RetryTestExecuter retryTestExecuter = createRetryTestExecuter(task, adapter, objectFactory); + RetryTestExecuter retryTestExecuter = createRetryTestExecuter(task, adapter, objectFactory, lastResultHolder); setTestExecuter(task, retryTestExecuter); } } diff --git a/plugin/src/main/java/org/gradle/testretry/internal/executer/LastResultHolder.java b/plugin/src/main/java/org/gradle/testretry/internal/executer/LastResultHolder.java new file mode 100644 index 00000000..3f6907b0 --- /dev/null +++ b/plugin/src/main/java/org/gradle/testretry/internal/executer/LastResultHolder.java @@ -0,0 +1,17 @@ +package org.gradle.testretry.internal.executer; + +import javax.annotation.Nullable; + +public class LastResultHolder { + @Nullable + private RoundResult lastResult; + + @Nullable + public RoundResult get() { + return lastResult; + } + + public void set(RoundResult lastResult) { + this.lastResult = lastResult; + } +} diff --git a/plugin/src/main/java/org/gradle/testretry/internal/executer/RetryTestExecuter.java b/plugin/src/main/java/org/gradle/testretry/internal/executer/RetryTestExecuter.java index ad5b999d..3fddf3b0 100644 --- a/plugin/src/main/java/org/gradle/testretry/internal/executer/RetryTestExecuter.java +++ b/plugin/src/main/java/org/gradle/testretry/internal/executer/RetryTestExecuter.java @@ -32,7 +32,6 @@ import java.io.File; import java.util.Set; -import java.util.stream.Collectors; import static org.gradle.testretry.internal.executer.JvmTestExecutionSpecFactory.testExecutionSpecFor; @@ -44,7 +43,7 @@ public final class RetryTestExecuter implements TestExecuter testClassesDir, - Set resolvedClasspath + Set resolvedClasspath, + LastResultHolder lastResultHolder ) { this.extension = extension; this.delegate = delegate; this.testTask = task; + this.lastResultHolder = lastResultHolder; this.frameworkTemplate = new TestFrameworkTemplate( testTask, instantiator, @@ -115,7 +116,7 @@ public void execute(JvmTestExecutionSpec spec, TestResultProcessor testResultPro while (true) { delegate.execute(testExecutionSpec, retryTestResultProcessor); RoundResult result = retryTestResultProcessor.getResult(); - lastResult = result; + lastResultHolder.set(result); if (extension.getSimulateNotRetryableTest() || !result.nonRetriedTests.isEmpty()) { // fall through to our doLast action to fail accordingly @@ -136,19 +137,6 @@ public void execute(JvmTestExecutionSpec spec, TestResultProcessor testResultPro } } - public void failWithNonRetriedTestsIfAny() { - if (extension.getSimulateNotRetryableTest() || hasNonRetriedTests()) { - throw new IllegalStateException("org.gradle.test-retry was unable to retry the following test methods, which is unexpected. Please file a bug report at https://github.com/gradle/test-retry-gradle-plugin/issues" + - lastResult.nonRetriedTests.stream() - .flatMap(entry -> entry.getValue().stream().map(methodName -> " " + entry.getKey() + "#" + methodName)) - .collect(Collectors.joining("\n", "\n", "\n"))); - } - } - - private boolean hasNonRetriedTests() { - return lastResult != null && !lastResult.nonRetriedTests.isEmpty(); - } - @Override public void stopNow() { delegate.stopNow(); diff --git a/plugin/src/main/java/org/gradle/testretry/internal/executer/RoundResult.java b/plugin/src/main/java/org/gradle/testretry/internal/executer/RoundResult.java index 5beb5e06..edd6a7f1 100644 --- a/plugin/src/main/java/org/gradle/testretry/internal/executer/RoundResult.java +++ b/plugin/src/main/java/org/gradle/testretry/internal/executer/RoundResult.java @@ -15,12 +15,12 @@ */ package org.gradle.testretry.internal.executer; -final class RoundResult { +public final class RoundResult { - final TestNames failedTests; - final TestNames nonRetriedTests; - final boolean lastRound; - final boolean hasRetryFilteredFailures; + public final TestNames failedTests; + public final TestNames nonRetriedTests; + public final boolean lastRound; + public final boolean hasRetryFilteredFailures; RoundResult( TestNames failedTests,