This is an automated email from the ASF dual-hosted git repository. michaelo pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/maven-surefire.git
The following commit(s) were added to refs/heads/master by this push: new e1f94a028 [SUREFIRE-2276] JUnit5's TestTemplate failures treated as flakes with retries e1f94a028 is described below commit e1f94a0281f0db1f9169e06b7ad149a27afd3c46 Author: Hugo G <hgrzeskow...@atlassian.com> AuthorDate: Thu Sep 26 21:27:04 2024 +1000 [SUREFIRE-2276] JUnit5's TestTemplate failures treated as flakes with retries This closes #788 --- .../maven/surefire/its/JUnitPlatformEnginesIT.java | 39 ++++++++++++++ .../test/resources/junit5-testtemplate-bug/pom.xml | 55 +++++++++++++++++++ .../src/test/java/pkg/FieldSettingTest.java | 63 ++++++++++++++++++++++ .../src/test/java/pkg/ParamsContextTest.java | 62 +++++++++++++++++++++ .../surefire/junitplatform/RunListenerAdapter.java | 4 +- 5 files changed, 222 insertions(+), 1 deletion(-) diff --git a/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformEnginesIT.java b/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformEnginesIT.java index ba79ccb9f..255aea291 100644 --- a/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformEnginesIT.java +++ b/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformEnginesIT.java @@ -296,6 +296,45 @@ public class JUnitPlatformEnginesIT extends SurefireJUnit4IntegrationTestCase { .assertContainsText("Encountered failure in TestTemplate provideTestTemplateInvocationContexts()"); } + @Test + public void testJupiterEngineWithTestTemplateNotClassifiedAsFlake() { + unpack("junit5-testtemplate-bug", "-" + jupiter) + .setTestToRun("FieldSettingTest") + .sysProp("junit5.version", jupiter) + .maven() + .withFailure() + .executeTest() + .verifyTextInLog("AssertionFailedError") + .assertTestSuiteResults(2, 0, 1, 0, 0); + + unpack("junit5-testtemplate-bug", "-" + jupiter) + .debugLogging() + .setTestToRun("FieldSettingTest") + .sysProp("junit5.version", jupiter) + // The tests are failing deterministically, so rerunning them should not change the result + .sysProp("surefire.rerunFailingTestsCount", "1") + .maven() + .withFailure() + .executeTest() + .verifyTextInLog("AssertionFailedError") + .assertTestSuiteResults(2, 0, 1, 0, 0); + } + + @Test + public void testJupiterEngineWithParameterizedTestsNotClassifiedAsFlake() { + unpack("junit5-testtemplate-bug", "-" + jupiter) + .debugLogging() + .setTestToRun("ParamsContextTest") + .sysProp("junit5.version", jupiter) + // The tests are failing deterministically, so rerunning them should not change the result + .sysProp("surefire.rerunFailingTestsCount", "1") + .maven() + .withFailure() + .executeTest() + .verifyTextInLog("AssertionFailedError") + .assertTestSuiteResults(2, 0, 1, 0, 0); + } + @Test public void testJupiterEngineWithAssertionsFailNoParameters() { // `Assertions.fail()` not supported until 5.2.0 diff --git a/surefire-its/src/test/resources/junit5-testtemplate-bug/pom.xml b/surefire-its/src/test/resources/junit5-testtemplate-bug/pom.xml new file mode 100644 index 000000000..335ba72b5 --- /dev/null +++ b/surefire-its/src/test/resources/junit5-testtemplate-bug/pom.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>org.apache.maven.plugins.surefire</groupId> + <artifactId>surefire-junit-testtemplate-retry-bug</artifactId> + <version>1.0-SNAPSHOT</version> + <name>Test for JUnit 5 TestTemplate with retries</name> + + <properties> + <maven.compiler.source>${java.specification.version}</maven.compiler.source> + <maven.compiler.target>${java.specification.version}</maven.compiler.target> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <dependencies> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <version>${junit5.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <version>${junit5.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <version>${junit5.version}</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.8.1</version> + <configuration> + <encoding>UTF-8</encoding> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>${surefire.version}</version> + </plugin> + </plugins> + </build> + +</project> diff --git a/surefire-its/src/test/resources/junit5-testtemplate-bug/src/test/java/pkg/FieldSettingTest.java b/surefire-its/src/test/resources/junit5-testtemplate-bug/src/test/java/pkg/FieldSettingTest.java new file mode 100644 index 000000000..61f5fa5c5 --- /dev/null +++ b/surefire-its/src/test/resources/junit5-testtemplate-bug/src/test/java/pkg/FieldSettingTest.java @@ -0,0 +1,63 @@ +package pkg; + +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.Extension; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.TestTemplateInvocationContext; +import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class FieldSettingTest { + private int testValue = 42; + + // We're calling this in the provider underneath + public void setTestValue(int testValue) { + this.testValue = testValue; + } + + @TestTemplate + @ExtendWith(FieldSettingContextProvider.class) + public void testTemplatePartiallyFails() { + assertEquals(42, testValue); + } +} + + +class FieldSettingContextProvider implements TestTemplateInvocationContextProvider { + @Override + public boolean supportsTestTemplate(ExtensionContext extensionContext) { + return true; + } + + @Override + public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext extensionContext) { + return Stream.of(context(0), context(42)); + } + + private TestTemplateInvocationContext context(int parameter) { + return new TestTemplateInvocationContext() { + @Override + public String getDisplayName(int invocationIndex) { + return String.format("[%d] %s", invocationIndex, parameter); + } + + @Override + public List<Extension> getAdditionalExtensions() { + return getBeforeEachCallbacks(parameter); + } + }; + } + + private List<Extension> getBeforeEachCallbacks(int value) { + return Arrays.asList((BeforeEachCallback) ctx -> + ((FieldSettingTest) ctx.getRequiredTestInstance()).setTestValue(value) + ); + } +} diff --git a/surefire-its/src/test/resources/junit5-testtemplate-bug/src/test/java/pkg/ParamsContextTest.java b/surefire-its/src/test/resources/junit5-testtemplate-bug/src/test/java/pkg/ParamsContextTest.java new file mode 100644 index 000000000..4022e2cbb --- /dev/null +++ b/surefire-its/src/test/resources/junit5-testtemplate-bug/src/test/java/pkg/ParamsContextTest.java @@ -0,0 +1,62 @@ +package pkg; + +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.Extension; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolver; +import org.junit.jupiter.api.extension.TestTemplateInvocationContext; +import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ParamsContextTest { + + @TestTemplate + @ExtendWith(ParamsContextProvider.class) + void testTemplatePartiallyFails(int value) { + assertEquals(42, value); + } +} + +class ParamsContextProvider implements TestTemplateInvocationContextProvider { + + @Override + public boolean supportsTestTemplate(ExtensionContext context) { + return true; + } + + @Override + public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) { + return Stream.of(invocationContext(0), invocationContext(42)); + } + + private TestTemplateInvocationContext invocationContext(int parameter) { + return new TestTemplateInvocationContext() { + @Override + public String getDisplayName(int invocationIndex) { + return String.format("[%d] %s", invocationIndex, parameter); + } + + @Override + public List<Extension> getAdditionalExtensions() { + return Collections.singletonList(new ParameterResolver() { + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return true; + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return parameter; + } + }); + } + }; + } +} diff --git a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/RunListenerAdapter.java b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/RunListenerAdapter.java index 8c274fd1c..7ae8cc79d 100644 --- a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/RunListenerAdapter.java +++ b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/RunListenerAdapter.java @@ -317,12 +317,14 @@ final class RunListenerAdapter implements TestExecutionListener, TestOutputRecei boolean needsSpaceSeparator = isNotBlank(parentDisplay) && !display.startsWith("["); String methodDisplay = parentDisplay + (needsSpaceSeparator ? " " : "") + display; + boolean isParameterized = isNotBlank(methodSource.getMethodParameterTypes()); boolean hasParameterizedParent = collectAllTestIdentifiersInHierarchy(testIdentifier) .filter(identifier -> !identifier.getSource().isPresent()) .map(TestIdentifier::getLegacyReportingName) .anyMatch(legacyReportingName -> legacyReportingName.matches("^\\[.+]$")); + boolean isTestTemplate = testIdentifier.getLegacyReportingName().matches("^.*\\[\\d+]$"); - boolean parameterized = isNotBlank(methodSource.getMethodParameterTypes()) || hasParameterizedParent; + boolean parameterized = isParameterized || hasParameterizedParent || isTestTemplate; String methodName = methodSource.getMethodName(); String description = testIdentifier.getLegacyReportingName(); boolean equalDescriptions = methodDisplay.equals(description);