Skip to content

Commit ab7e1be

Browse files
authored
Delete files in read-only dirs by making them writable (#2266)
Fixes #2171.
1 parent 02e376b commit ab7e1be

File tree

3 files changed

+61
-1
lines changed

3 files changed

+61
-1
lines changed

documentation/src/docs/asciidoc/release-notes/release-notes-5.7.0-M1.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ on GitHub.
4545

4646
==== Bug Fixes
4747

48+
* `@TempDir` is now able to clean up files in read-only directories.
4849
* The Jupiter engine now ignores `MethodSelectors` for methods in non-Jupiter test
4950
classes instead of failing for missing methods in such cases.
5051

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ private FileVisitResult deleteAndContinue(Path path) {
219219

220220
private void makeWritableAndTryToDeleteAgain(Path path, IOException exception) {
221221
try {
222+
tryToMakeParentDirsWritable(path);
222223
makeWritable(path);
223224
Files.delete(path);
224225
}
@@ -228,6 +229,18 @@ private void makeWritableAndTryToDeleteAgain(Path path, IOException exception) {
228229
}
229230
}
230231

232+
private void tryToMakeParentDirsWritable(Path path) {
233+
Path relativePath = dir.relativize(path);
234+
Path parent = dir;
235+
for (int i = 0; i < relativePath.getNameCount(); i++) {
236+
boolean writable = parent.toFile().setWritable(true);
237+
if (!writable) {
238+
break;
239+
}
240+
parent = parent.resolve(relativePath.getName(i));
241+
}
242+
}
243+
231244
private void makeWritable(Path path) {
232245
boolean writable = path.toFile().setWritable(true);
233246
if (!writable) {

junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryTests.java

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
import org.junit.jupiter.api.TestInfo;
4646
import org.junit.jupiter.api.TestInstance;
4747
import org.junit.jupiter.api.TestMethodOrder;
48+
import org.junit.jupiter.api.condition.DisabledOnOs;
49+
import org.junit.jupiter.api.condition.OS;
4850
import org.junit.jupiter.api.extension.ExtensionConfigurationException;
4951
import org.junit.jupiter.api.extension.ParameterResolutionException;
5052
import org.junit.jupiter.api.io.TempDir;
@@ -84,11 +86,27 @@ void tempDirectoryDoesNotPreventUserFromDeletingTempDir() {
8486

8587
@Test
8688
@DisplayName("is capable of removal of a read-only file")
87-
void nonWritableFileDoesNotCauseFailureTestCase() {
89+
void nonWritableFileDoesNotCauseFailure() {
8890
executeTestsForClass(NonWritableFileDoesNotCauseFailureTestCase.class).testEvents()//
8991
.assertStatistics(stats -> stats.started(1).succeeded(1));
9092
}
9193

94+
@Test
95+
@DisabledOnOs(OS.WINDOWS)
96+
@DisplayName("is capable of removal of a read-only file in a read-only dir")
97+
void readOnlyFileInReadOnlyDirDoesNotCauseFailure() {
98+
executeTestsForClass(ReadOnlyFileInReadOnlyDirDoesNotCauseFailureTestCase.class).testEvents()//
99+
.assertStatistics(stats -> stats.started(1).succeeded(1));
100+
}
101+
102+
@Test
103+
@DisabledOnOs(OS.WINDOWS)
104+
@DisplayName("is capable of removal of a read-only file in a dir in a read-only dir")
105+
void readOnlyFileInNestedReadOnlyDirDoesNotCauseFailure() {
106+
executeTestsForClass(ReadOnlyFileInDirInReadOnlyDirDoesNotCauseFailureTestCase.class).testEvents()//
107+
.assertStatistics(stats -> stats.started(1).succeeded(1));
108+
}
109+
92110
@Test
93111
@DisplayName("can be used via instance field inside nested test classes")
94112
void canBeUsedViaInstanceFieldInsideNestedTestClasses() {
@@ -780,6 +798,34 @@ void createReadonlyFile(@TempDir Path tempDir) throws IOException {
780798

781799
}
782800

801+
// https://github.com/junit-team/junit5/issues/2171
802+
static class ReadOnlyFileInReadOnlyDirDoesNotCauseFailureTestCase {
803+
804+
@Test
805+
void createReadOnlyFileInReadOnlyDir(@TempDir File tempDir) throws IOException {
806+
File file = tempDir.toPath().resolve("file").toFile();
807+
assumeTrue(file.createNewFile());
808+
assumeTrue(tempDir.setReadOnly());
809+
assumeTrue(file.setReadOnly());
810+
}
811+
812+
}
813+
814+
// https://github.com/junit-team/junit5/issues/2171
815+
static class ReadOnlyFileInDirInReadOnlyDirDoesNotCauseFailureTestCase {
816+
817+
@Test
818+
void createReadOnlyFileInReadOnlyDir(@TempDir File tempDir) throws IOException {
819+
File file = tempDir.toPath().resolve("dir").resolve("file").toFile();
820+
assumeTrue(file.getParentFile().mkdirs());
821+
assumeTrue(file.createNewFile());
822+
assumeTrue(tempDir.setReadOnly());
823+
assumeTrue(file.getParentFile().setReadOnly());
824+
assumeTrue(file.setReadOnly());
825+
}
826+
827+
}
828+
783829
// https://github.com/junit-team/junit5/issues/2079
784830
static class TempDirUsageInsideNestedClassesTestCase {
785831

0 commit comments

Comments
 (0)