diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.0-RC1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.0-RC1.adoc index e76d8c4020aa..dfa3165da404 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.0-RC1.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.0-RC1.adoc @@ -60,7 +60,8 @@ repository on GitHub. `assertInstanceOf` methods introduced in `5.8` version. * New generators in `DynamicTest` that take a `Stream`/`Iterator` of `Named` along with a convenient `NamedExecutable` interface that can simplify writing dynamic - tests, in particular in recent version of Java that support records. + tests, in particular in recent versions of Java that support records. +* `@TempDir` now fails fast when `TempDirFactory::createTempDirectory` returns `null` [[release-notes-5.11.0-RC1-junit-vintage]] diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java index f165b2e55968..f6b6d5c5dd1e 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java @@ -286,7 +286,8 @@ static class CloseablePath implements CloseableResource { CloseablePath(TempDirFactory factory, CleanupMode cleanupMode, AnnotatedElementContext elementContext, ExtensionContext extensionContext) throws Exception { - this.dir = factory.createTempDirectory(elementContext, extensionContext); + this.dir = Preconditions.notNull(factory.createTempDirectory(elementContext, extensionContext), + "temp directory must not be null"); this.factory = factory; this.cleanupMode = cleanupMode; this.extensionContext = extensionContext; diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerContextTests.java b/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerContextTests.java index 49b28d2dcc3a..3a098cfa5875 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerContextTests.java +++ b/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerContextTests.java @@ -54,14 +54,15 @@ import org.junit.jupiter.api.extension.ParameterResolutionException; import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.api.io.TempDirFactory; +import org.junit.jupiter.api.io.TempDirFactory.Standard; import org.junit.jupiter.engine.AbstractJupiterTestEngineTests; +import org.junit.platform.commons.PreconditionViolationException; import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder; import org.junit.platform.testkit.engine.EngineExecutionResults; -import org.junit.platform.testkit.engine.Events; /** * Integration tests for the legacy behavior of the {@link TempDirectory} - * extension to create a single temp directory per context, i.e. test class or + * extension to create a single temp directory per context, i.e., test class or * method. * * @since 5.4 @@ -74,6 +75,13 @@ protected EngineExecutionResults executeTestsForClass(Class testClass) { return executeTests(requestBuilder(testClass).build()); } + private EngineExecutionResults executeTestsForClassWithDefaultFactory(Class testClass, + Class factoryClass) { + return executeTests(requestBuilder(testClass) // + .configurationParameter(TempDir.DEFAULT_FACTORY_PROPERTY_NAME, factoryClass.getName()) // + .build()); + } + @SuppressWarnings("deprecation") private static LauncherDiscoveryRequestBuilder requestBuilder(Class testClass) { return request() // @@ -295,25 +303,20 @@ void resolvesSeparateTempDirWhenAnnotationIsUsedOnAfterAllMethodParameterOnly() @TestMethodOrder(OrderAnnotation.class) class DefaultFactory { - private Events executeTestsForClassWithDefaultFactory(Class testClass, - Class factoryClass) { - return TempDirectoryPerContextTests.super.executeTests(requestBuilder(testClass) // - .configurationParameter(TempDir.DEFAULT_FACTORY_PROPERTY_NAME, factoryClass.getName()) // - .build()).testEvents(); - } - @Test @DisplayName("set to Jupiter's default") void supportsStandardDefaultFactory() { - executeTestsForClassWithDefaultFactory(StandardDefaultFactoryTestCase.class, TempDirFactory.Standard.class) // - .assertStatistics(stats -> stats.started(1).succeeded(1)); + var results = executeTestsForClassWithDefaultFactory(StandardDefaultFactoryTestCase.class, Standard.class); + + results.testEvents().assertStatistics(stats -> stats.started(1).succeeded(1)); } @Test @DisplayName("set to custom factory") void supportsCustomDefaultFactory() { - executeTestsForClassWithDefaultFactory(NonStandardDefaultFactoryTestCase.class, Factory.class) // - .assertStatistics(stats -> stats.started(1).succeeded(1)); + var results = executeTestsForClassWithDefaultFactory(CustomDefaultFactoryTestCase.class, Factory.class); + + results.testEvents().assertStatistics(stats -> stats.started(1).succeeded(1)); } private static class Factory implements TempDirFactory { @@ -359,8 +362,7 @@ void onlySupportsParametersOfTypePathAndFile() { var results = executeTestsForClass(InvalidTestCase.class); // @formatter:off - TempDirectoryPerContextTests.assertSingleFailedTest(results, - instanceOf(ParameterResolutionException.class), + assertSingleFailedTest(results, instanceOf(ParameterResolutionException.class), message(m -> m.matches("Failed to resolve parameter \\[java.lang.String .+] in method \\[.+]: .+")), cause( instanceOf(ExtensionConfigurationException.class), @@ -389,14 +391,13 @@ void doesNotSupportTempDirAnnotationOnConstructorParameterWithTestInstancePerCla } @Test - @DisplayName("when @TempDir factory is not Standard") + @DisplayName("when non-default @TempDir factory is set") @Order(32) - void onlySupportsStandardTempDirFactory() { - var results = executeTestsForClass(NonStandardFactoryTestCase.class); + void doesNotSupportNonDefaultTempDirFactory() { + var results = executeTestsForClass(NonDefaultFactoryTestCase.class); // @formatter:off - TempDirectoryPerContextTests.assertSingleFailedTest(results, - instanceOf(ParameterResolutionException.class), + assertSingleFailedTest(results, instanceOf(ParameterResolutionException.class), message(m -> m.matches("Failed to resolve parameter \\[.+] in method \\[.+]: .+")), cause( instanceOf(ExtensionConfigurationException.class), @@ -405,6 +406,35 @@ void onlySupportsStandardTempDirFactory() { // @formatter:on } + @Test + @DisplayName("when default @TempDir factory returns null") + @Order(33) + void doesNotSupportCustomDefaultTempDirFactoryReturningNull() { + var results = executeTestsForClassWithDefaultFactory(CustomDefaultFactoryReturningNullTestCase.class, + FactoryReturningNull.class); + + // @formatter:off + assertSingleFailedTest(results, instanceOf(ParameterResolutionException.class), + message(m -> m.matches("Failed to resolve parameter \\[.+] in method \\[.+]: .+")), + cause( + instanceOf(ExtensionConfigurationException.class), + message("Failed to create default temp directory"), + cause( + instanceOf(PreconditionViolationException.class), + message("temp directory must not be null") + ) + )); + // @formatter:on + } + + private static class FactoryReturningNull implements TempDirFactory { + + @Override + public Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext) { + return null; + } + } + } @Nested @@ -685,7 +715,7 @@ static class AnnotationOnConstructorParameterWithTestInstancePerClassTestCase } } - static class NonStandardFactoryTestCase { + static class NonDefaultFactoryTestCase { @Test void test(@SuppressWarnings("unused") @TempDir(factory = Factory.class) Path tempDir) { @@ -712,7 +742,7 @@ void test(@TempDir Path tempDir1, @TempDir Path tempDir2) { } - static class NonStandardDefaultFactoryTestCase { + static class CustomDefaultFactoryTestCase { @Test void test(@TempDir Path tempDir1, @TempDir Path tempDir2) { @@ -721,6 +751,15 @@ void test(@TempDir Path tempDir1, @TempDir Path tempDir2) { } + static class CustomDefaultFactoryReturningNullTestCase { + + @Test + void test(@SuppressWarnings("unused") @TempDir Path tempDir) { + // never called + } + + } + static class AnnotationOnBeforeAllMethodParameterTestCase extends BaseSharedTempDirParameterInjectionTestCase { @BeforeAll diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerDeclarationTests.java b/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerDeclarationTests.java index 153146a782af..df1951ce3799 100644 --- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerDeclarationTests.java +++ b/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerDeclarationTests.java @@ -83,6 +83,7 @@ import org.junit.jupiter.engine.AbstractJupiterTestEngineTests; import org.junit.jupiter.engine.Constants; import org.junit.jupiter.engine.extension.TempDirectory.FileOperations; +import org.junit.platform.commons.PreconditionViolationException; import org.junit.platform.engine.reporting.ReportEntry; import org.junit.platform.testkit.engine.EngineExecutionResults; @@ -95,6 +96,14 @@ @DisplayName("TempDirectory extension (per declaration)") class TempDirectoryPerDeclarationTests extends AbstractJupiterTestEngineTests { + private EngineExecutionResults executeTestsForClassWithDefaultFactory(Class testClass, + Class factoryClass) { + return executeTests(request() // + .selectors(selectClass(testClass)) // + .configurationParameter(TempDir.DEFAULT_FACTORY_PROPERTY_NAME, factoryClass.getName()) // + .build()); + } + @BeforeEach @AfterEach void resetStaticVariables() { @@ -272,8 +281,7 @@ void onlySupportsParametersOfTypePathAndFile() { var results = executeTestsForClass(InvalidTestCase.class); // @formatter:off - TempDirectoryPerDeclarationTests.assertSingleFailedTest(results, - instanceOf(ParameterResolutionException.class), + assertSingleFailedTest(results, instanceOf(ParameterResolutionException.class), message(m -> m.matches("Failed to resolve parameter \\[java.lang.String .+] in method \\[.+]: .+")), cause( instanceOf(ExtensionConfigurationException.class), @@ -301,6 +309,55 @@ void doesNotSupportTempDirAnnotationOnConstructorParameterWithTestInstancePerCla "@TempDir is not supported on constructor parameters. Please use field injection instead."); } + @Test + @DisplayName("when @TempDir factory returns null") + @Order(32) + void doesNotSupportTempDirFactoryReturningNull() { + var results = executeTestsForClass(FactoryReturningNullTestCase.class); + + // @formatter:off + assertSingleFailedTest(results, instanceOf(ParameterResolutionException.class), + message(m -> m.matches("Failed to resolve parameter \\[.+] in method \\[.+]: .+")), + cause( + instanceOf(ExtensionConfigurationException.class), + message("Failed to create default temp directory"), + cause( + instanceOf(PreconditionViolationException.class), + message("temp directory must not be null") + ) + )); + // @formatter:on + } + + @Test + @DisplayName("when default @TempDir factory returns null") + @Order(33) + void doesNotSupportCustomDefaultTempDirFactoryReturningNull() { + var results = executeTestsForClassWithDefaultFactory(CustomDefaultFactoryReturningNullTestCase.class, + FactoryReturningNull.class); + + // @formatter:off + assertSingleFailedTest(results, instanceOf(ParameterResolutionException.class), + message(m -> m.matches("Failed to resolve parameter \\[.+] in method \\[.+]: .+")), + cause( + instanceOf(ExtensionConfigurationException.class), + message("Failed to create default temp directory"), + cause( + instanceOf(PreconditionViolationException.class), + message("temp directory must not be null") + ) + )); + // @formatter:on + } + + private static class FactoryReturningNull implements TempDirFactory { + + @Override + public Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext) { + return null; + } + } + } @Nested @@ -379,14 +436,6 @@ void supportsFactoryWithCustomMetaAnnotation() { @TestMethodOrder(OrderAnnotation.class) class DefaultFactory { - private EngineExecutionResults executeTestsForClassWithDefaultFactory(Class testClass, - Class factoryClass) { - return TempDirectoryPerDeclarationTests.super.executeTests(request() // - .selectors(selectClass(testClass)) // - .configurationParameter(TempDir.DEFAULT_FACTORY_PROPERTY_NAME, factoryClass.getName()) // - .build()); - } - @Test @DisplayName("set to Jupiter's default") void supportsStandardDefaultFactory() { @@ -1369,6 +1418,23 @@ public Path createTempDirectory(AnnotatedElementContext elementContext, Extensio } + static class FactoryReturningNullTestCase { + + @Test + void test(@SuppressWarnings("unused") @TempDir(factory = Factory.class) Path tempDir) { + // never called + } + + private static class Factory implements TempDirFactory { + + @Override + public Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext) { + return null; + } + } + + } + static class StandardDefaultFactoryTestCase { @Test @@ -1402,4 +1468,13 @@ void test(@TempDir Path tempDir1, @TempDir(factory = Standard.class) Path tempDi } + static class CustomDefaultFactoryReturningNullTestCase { + + @Test + void test(@SuppressWarnings("unused") @TempDir Path tempDir) { + // never called + } + + } + }