From 00f964b618a8de41dd5a47c2783fc34e816a8f39 Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Sun, 16 Apr 2023 16:36:01 +0200 Subject: [PATCH] Include root cause when failing to delete file Fixes #3236. --- .../release-notes-5.10.0-M1.adoc | 2 + .../engine/extension/TempDirectory.java | 1 + .../TempDirectoryPerDeclarationTests.java | 51 +++++++++++++++++-- 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0-M1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0-M1.adoc index 3d0d3aea2375..e062fb949960 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0-M1.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0-M1.adoc @@ -60,6 +60,8 @@ will exclude all methods called `methodName` under package `org.example`. `@MethodSource("myFactory([I)"` (which was already supported) and `@MethodSource("myFactory(java.lang.String[])` instead of `@MethodSource("myFactory([Ljava.lang.String;)`. +* Exceptions thrown for undeletable files when cleaning up a temporary directory created + via `@TempDir` now include the root cause. ==== Deprecations and Breaking Changes 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 ba5f53d71971..9fc7a37dc4c4 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 @@ -319,6 +319,7 @@ private void resetPermissionsAndTryToDeleteAgain(Path path, IOException exceptio } catch (Exception suppressed) { exception.addSuppressed(suppressed); + failures.put(path, exception); } } else { 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 6cc8336bb202..100bbe939499 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 @@ -29,6 +29,7 @@ import java.io.File; import java.io.IOException; +import java.nio.file.DirectoryNotEmptyException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; @@ -55,7 +56,6 @@ import org.junit.jupiter.api.condition.DisabledOnOs; import org.junit.jupiter.api.condition.OS; import org.junit.jupiter.api.extension.BeforeEachCallback; -import org.junit.jupiter.api.extension.Extension; import org.junit.jupiter.api.extension.ExtensionConfigurationException; import org.junit.jupiter.api.extension.ParameterResolutionException; import org.junit.jupiter.api.extension.RegisterExtension; @@ -200,7 +200,19 @@ void onlyAttemptsToDeleteUndeletableDirectoriesOnce() { assertSingleFailedTest(results, // instanceOf(IOException.class), // message(it -> it.startsWith("Failed to delete temp directory")), // - suppressed(0, instanceOf(IOException.class), message("Simulated failure")), // + suppressed(0, instanceOf(DirectoryNotEmptyException.class)), // + suppressed(1, instanceOf(IOException.class), message("Simulated failure"))); + } + + @Test + @DisplayName("only attempts to delete undeletable files once") + void onlyAttemptsToDeleteUndeletableFilesOnce() { + var results = executeTestsForClass(UndeletableFileTestCase.class); + + assertSingleFailedTest(results, // + instanceOf(IOException.class), // + message(it -> it.startsWith("Failed to delete temp directory")), // + suppressed(0, instanceOf(DirectoryNotEmptyException.class)), // suppressed(1, instanceOf(IOException.class), message("Simulated failure"))); } @@ -1014,16 +1026,45 @@ private static Map getTempDirs(TestInfo testInfo) { static class UndeletableDirectoryTestCase { + static final Path UNDELETABLE_SUB_DIR = Path.of("undeletable"); + @RegisterExtension - Extension injector = (BeforeEachCallback) context -> context // + BeforeEachCallback injector = context -> context // .getStore(TempDirectory.NAMESPACE) // .put(TempDirectory.FILE_OPERATIONS_KEY, (FileOperations) path -> { - throw new IOException("Simulated failure"); + if (path.endsWith(UNDELETABLE_SUB_DIR)) { + throw new IOException("Simulated failure"); + } + else { + Files.delete(path); + } }); @Test void test(@TempDir Path tempDir) throws Exception { - Files.createDirectory(tempDir.resolve("test-sub-dir")); + Files.createDirectory(tempDir.resolve(UNDELETABLE_SUB_DIR)); + } + } + + static class UndeletableFileTestCase { + + static final Path UNDELETABLE_FILE = Path.of("undeletable"); + + @RegisterExtension + BeforeEachCallback injector = context -> context // + .getStore(TempDirectory.NAMESPACE) // + .put(TempDirectory.FILE_OPERATIONS_KEY, (FileOperations) path -> { + if (path.endsWith(UNDELETABLE_FILE)) { + throw new IOException("Simulated failure"); + } + else { + Files.delete(path); + } + }); + + @Test + void test(@TempDir Path tempDir) throws Exception { + Files.createFile(tempDir.resolve(UNDELETABLE_FILE)); } } }