Skip to content

Commit

Permalink
Feature/temp dir cleanup (junit-team#2729)
Browse files Browse the repository at this point in the history
This PR adds a cleanup mode (`ALWAYS` (default), `ON_SUCCESS`, or
`NEVER`) parameter to `@TempDir` that allows to configured when the
temp directory should be deleted. The default can be configured by
setting a configuration parameter.

Resolves junit-team#2159.

Co-authored-by: Marc Philipp <mail@marcphilipp.de>
  • Loading branch information
2 people authored and runningcode committed Feb 15, 2023
1 parent 07bcf51 commit b11016d
Show file tree
Hide file tree
Showing 16 changed files with 724 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,6 @@ on GitHub.

==== New Features and Improvements

* ❓
* `@TempDir` now includes a cleanup mode attribute for preventing a temporary directory
from being deleted after a test. The default cleanup mode can be configured via a
configuration parameter.
15 changes: 15 additions & 0 deletions documentation/src/docs/asciidoc/user-guide/writing-tests.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2349,3 +2349,18 @@ method uses a separate directory.
----
include::{testDir}/example/TempDirectoryDemo.java[tags=user_guide_field_injection]
----

The `@TempDir` annotation has an optional `cleanup` attribute that can be set to either
`NEVER`, `ON_SUCCESS`, or `ALWAYS`. If the cleanup mode is set to `NEVER`, temporary
directories are not deleted after a test completes. If it is set to `ON_SUCCESS`,
temporary directories are deleted only after a test completed successfully.

The default cleanup mode is `ALWAYS`. You can use the
`junit.jupiter.temp.dir.cleanup.mode.default`
<<running-tests-config-params, configuration parameter>> to override this default.

[source,java,indent=0]
.A test class with a temporary directory that doesn't get cleaned up
----
include::{testDir}/example/TempDirCleanupModeDemo.java[tags=user_guide]
----
28 changes: 28 additions & 0 deletions documentation/src/test/java/example/TempDirCleanupModeDemo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2015-2021 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v2.0 which
* accompanies this distribution and is available at
*
* https://www.eclipse.org/legal/epl-v20.html
*/

package example;

import static org.junit.jupiter.api.io.CleanupMode.ON_SUCCESS;

import java.nio.file.Path;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

// tag::user_guide[]
class TempDirCleanupModeDemo {

@Test
void fileTest(@TempDir(cleanup = ON_SUCCESS) Path tempDir) {
// perform test
}
}
// end::user_guide[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2015-2021 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v2.0 which
* accompanies this distribution and is available at
*
* https://www.eclipse.org/legal/epl-v20.html
*/

package org.junit.jupiter.api.io;

import static org.apiguardian.api.API.Status.EXPERIMENTAL;

import org.apiguardian.api.API;

/**
* Enumeration of cleanup modes for a {@code TempDir}.
*
* <p>When a test with a temporary directory completes, it might be useful in
* some cases to be able to view the contents of the directory resulting from
* the test. {@code CleanupMode} allows control of how a {@code TempDir}
* is cleaned up.
*
* @since 5.4
* @see TempDir
*/
@API(status = EXPERIMENTAL, since = "5.4")
public enum CleanupMode {

/**
* Defer to the configured cleanup mode.
*/
DEFAULT,

/**
* Always clean up a temporary directory after the test has completed.
*/
ALWAYS,

/**
* Don't clean up a temporary directory after the test has completed.
*/
NEVER,

/**
* Only clean up a temporary directory if the test completed successfully.
*/
ON_SUCCESS
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,35 @@
*
* <h3>Deletion</h3>
*
* <p>When the end of the scope of a temporary directory is reached, i.e. when
* the test method or class has finished execution, JUnit will attempt to
* recursively delete all files and directories in the temporary directory
* <p>By default, when the end of the scope of a temporary directory is reached,
* i.e. when the test method or class has finished execution, JUnit will attempt
* to recursively delete all files and directories in the temporary directory
* and, finally, the temporary directory itself. In case deletion of a file or
* directory fails, an {@link IOException} will be thrown that will cause the
* test or test class to fail.
*
* <p>The {@code @TempDir} annotation has a {@link CleanupMode} parameter that
* allows overriding the default behavior. If the cleanup mode is set to
* {@link CleanupMode#NEVER}, then the temporary directory will not be deleted
* after the test completes. If the cleanup mode is set to
* {@link CleanupMode#ON_SUCCESS}, then the temporary directory will only be
* deleted if the test completes successfully. The default behavior can be
* altered by setting the {@value #DEFAULT_CLEANUP_MODE_PROPERTY_NAME}
* configuration parameter.
*
* @since 5.4
*/
@Target({ ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(status = EXPERIMENTAL, since = "5.4")
public @interface TempDir {

String DEFAULT_CLEANUP_MODE_PROPERTY_NAME = "junit.jupiter.cleanup.mode.default";

/**
* How the temporary directory gets cleaned up after the test completes.
*/
CleanupMode cleanup() default CleanupMode.DEFAULT;

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
package org.junit.jupiter.engine.config;

import static org.apiguardian.api.API.Status.INTERNAL;
import static org.junit.jupiter.api.io.TempDir.DEFAULT_CLEANUP_MODE_PROPERTY_NAME;

import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
Expand All @@ -23,6 +24,7 @@
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.extension.ExecutionCondition;
import org.junit.jupiter.api.io.CleanupMode;
import org.junit.jupiter.api.parallel.ExecutionMode;

/**
Expand Down Expand Up @@ -107,4 +109,10 @@ public Optional<ClassOrderer> getDefaultTestClassOrderer() {
key -> delegate.getDefaultTestClassOrderer());
}

@Override
public CleanupMode getDefaultTempDirCleanupMode() {
return (CleanupMode) cache.computeIfAbsent(DEFAULT_CLEANUP_MODE_PROPERTY_NAME,
key -> delegate.getDefaultTempDirCleanupMode());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
package org.junit.jupiter.engine.config;

import static org.apiguardian.api.API.Status.INTERNAL;
import static org.junit.jupiter.api.io.CleanupMode.ALWAYS;
import static org.junit.jupiter.api.io.TempDir.DEFAULT_CLEANUP_MODE_PROPERTY_NAME;

import java.util.Optional;
import java.util.function.Function;
Expand All @@ -22,6 +24,7 @@
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.jupiter.api.extension.ExecutionCondition;
import org.junit.jupiter.api.io.CleanupMode;
import org.junit.jupiter.api.parallel.ExecutionMode;
import org.junit.platform.commons.util.ClassNamePatternFilterUtils;
import org.junit.platform.commons.util.Preconditions;
Expand Down Expand Up @@ -50,6 +53,9 @@ public class DefaultJupiterConfiguration implements JupiterConfiguration {
private static final InstantiatingConfigurationParameterConverter<ClassOrderer> classOrdererConverter = //
new InstantiatingConfigurationParameterConverter<>(ClassOrderer.class, "class orderer");

private static final EnumConfigurationParameterConverter<CleanupMode> cleanupModeConverter = //
new EnumConfigurationParameterConverter<>(CleanupMode.class, "cleanup mode");

private final ConfigurationParameters configurationParameters;

public DefaultJupiterConfiguration(ConfigurationParameters configurationParameters) {
Expand Down Expand Up @@ -117,4 +123,9 @@ public Optional<ClassOrderer> getDefaultTestClassOrderer() {
return classOrdererConverter.get(configurationParameters, DEFAULT_TEST_CLASS_ORDER_PROPERTY_NAME);
}

@Override
public CleanupMode getDefaultTempDirCleanupMode() {
return cleanupModeConverter.get(configurationParameters, DEFAULT_CLEANUP_MODE_PROPERTY_NAME, ALWAYS);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.extension.ExecutionCondition;
import org.junit.jupiter.api.io.CleanupMode;
import org.junit.jupiter.api.parallel.ExecutionMode;
import org.junit.platform.commons.util.ClassNamePatternFilterUtils;

Expand All @@ -42,7 +43,6 @@ public interface JupiterConfiguration {
String DEFAULT_TEST_METHOD_ORDER_PROPERTY_NAME = "junit.jupiter.testmethod.order.default";
String DEFAULT_TEST_CLASS_ORDER_PROPERTY_NAME = "junit.jupiter.testclass.order.default";
String TEMP_DIR_SCOPE_PROPERTY_NAME = "junit.jupiter.tempdir.scope";

String DEFAULT_TIMEOUT_PROPERTY_NAME = "junit.jupiter.execution.timeout.default";
String DEFAULT_TESTABLE_METHOD_TIMEOUT_PROPERTY_NAME = "junit.jupiter.execution.timeout.testable.method.default";
String DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME = "junit.jupiter.execution.timeout.test.method.default";
Expand Down Expand Up @@ -77,4 +77,6 @@ public interface JupiterConfiguration {

Optional<ClassOrderer> getDefaultTestClassOrderer();

CleanupMode getDefaultTempDirCleanupMode();

}
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,8 @@ public class MutableExtensionRegistry implements ExtensionRegistry, ExtensionReg

private static final Logger logger = LoggerFactory.getLogger(MutableExtensionRegistry.class);

private static final List<Extension> DEFAULT_EXTENSIONS = Collections.unmodifiableList(Arrays.asList(//
private static final List<Extension> DEFAULT_STATELESS_EXTENSIONS = Collections.unmodifiableList(Arrays.asList(//
new DisabledCondition(), //
new TempDirectory(), //
new TimeoutExtension(), //
new RepeatedTestExtension(), //
new TestInfoParameterResolver(), //
Expand All @@ -71,7 +70,9 @@ public class MutableExtensionRegistry implements ExtensionRegistry, ExtensionReg
public static MutableExtensionRegistry createRegistryWithDefaultExtensions(JupiterConfiguration configuration) {
MutableExtensionRegistry extensionRegistry = new MutableExtensionRegistry(null);

DEFAULT_EXTENSIONS.forEach(extensionRegistry::registerDefaultExtension);
DEFAULT_STATELESS_EXTENSIONS.forEach(extensionRegistry::registerDefaultExtension);

extensionRegistry.registerDefaultExtension(new TempDirectory(configuration));

if (configuration.isExtensionAutoDetectionEnabled()) {
registerAutoDetectedExtensions(extensionRegistry);
Expand Down
Loading

0 comments on commit b11016d

Please sign in to comment.