Skip to content

Commit

Permalink
Let @TempDir fail fast with File annotated element and non-defaul…
Browse files Browse the repository at this point in the history
…t file system temp directory (#3921)

Closes #3910.

---------

Co-authored-by: Marc Philipp <[email protected]>
  • Loading branch information
scordio and marcphilipp authored Aug 12, 2024
1 parent 57f1ad4 commit 4dbd0f9
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,9 @@ on GitHub.
tests, in particular in recent versions of Java that support records.
* `@TempDir` now fails fast in case `TempDirFactory::createTempDirectory` returns
`null`, a file, or a symbolic link to a file.
* `@TempDir` now fails fast in case the annotated target is of type `File` and
`TempDirFactory::createTempDirectory` returns a `Path` that does not belong to the
default file system.


[[release-notes-5.11.0-junit-vintage]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,24 @@
*
* <p>The temporary directory is only created if a field in a test class or a
* parameter in a lifecycle method or test method is annotated with
* {@code @TempDir}. If the field type or parameter type is neither {@link Path}
* nor {@link File}, if a field is declared as {@code final}, or if the temporary
* directory cannot be created, an {@link ExtensionConfigurationException} or a
* {@link ParameterResolutionException} will be thrown as appropriate. In
* addition, a {@code ParameterResolutionException} will be thrown for a
* {@code @TempDir}.
* An {@link ExtensionConfigurationException} or a
* {@link ParameterResolutionException} will be thrown in one of the following
* cases:
*
* <ul>
* <li>If the field type or parameter type is neither {@link Path} nor
{@link File}.</li>
* <li>If a field is declared as {@code final}.</li>
* <li>If the temporary directory cannot be created.</li>
* <li>If the field type or parameter type is {@code File} and a custom
* {@linkplain TempDir#factory() factory} is used, which creates a temporary
* directory that does not belong to the
* {@linkplain java.nio.file.FileSystems#getDefault() default file system}.
* </li>
* </ul>
*
* In addition, a {@code ParameterResolutionException} will be thrown for a
* constructor parameter annotated with {@code @TempDir}.
*
* <h2>Scope</h2>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.lang.reflect.Field;
import java.lang.reflect.Parameter;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
Expand Down Expand Up @@ -145,7 +146,7 @@ private void injectFields(ExtensionContext context, Object testInstance, Class<?
CleanupMode cleanupMode = determineCleanupModeForField(field);
TempDirFactory factory = determineTempDirFactoryForField(field, scope);
makeAccessible(field).set(testInstance,
getPathOrFile(new FieldContext(field), field.getType(), factory, cleanupMode, scope, context));
getPathOrFile(field.getType(), new FieldContext(field), factory, cleanupMode, scope, context));
}
catch (Throwable t) {
throw ExceptionUtils.throwAsUncheckedException(t);
Expand Down Expand Up @@ -178,7 +179,7 @@ public Object resolveParameter(ParameterContext parameterContext, ExtensionConte
CleanupMode cleanupMode = determineCleanupModeForParameter(parameterContext);
Scope scope = getScope(extensionContext);
TempDirFactory factory = determineTempDirFactoryForParameter(parameterContext, scope);
return getPathOrFile(parameterContext, parameterType, factory, cleanupMode, scope, extensionContext);
return getPathOrFile(parameterType, parameterContext, factory, cleanupMode, scope, extensionContext);
}

private CleanupMode determineCleanupModeForField(Field field) {
Expand Down Expand Up @@ -248,23 +249,24 @@ private void assertSupportedType(String target, Class<?> type) {
}
}

private Object getPathOrFile(AnnotatedElementContext elementContext, Class<?> type, TempDirFactory factory,
private Object getPathOrFile(Class<?> elementType, AnnotatedElementContext elementContext, TempDirFactory factory,
CleanupMode cleanupMode, Scope scope, ExtensionContext extensionContext) {
Namespace namespace = scope == Scope.PER_DECLARATION //
? NAMESPACE.append(elementContext) //
: NAMESPACE;
Path path = extensionContext.getStore(namespace) //
.getOrComputeIfAbsent(KEY, __ -> createTempDir(factory, cleanupMode, elementContext, extensionContext),
.getOrComputeIfAbsent(KEY,
__ -> createTempDir(factory, cleanupMode, elementType, elementContext, extensionContext),
CloseablePath.class) //
.get();

return (type == Path.class) ? path : path.toFile();
return (elementType == Path.class) ? path : path.toFile();
}

static CloseablePath createTempDir(TempDirFactory factory, CleanupMode cleanupMode,
static CloseablePath createTempDir(TempDirFactory factory, CleanupMode cleanupMode, Class<?> elementType,
AnnotatedElementContext elementContext, ExtensionContext extensionContext) {
try {
return new CloseablePath(factory, cleanupMode, elementContext, extensionContext);
return new CloseablePath(factory, cleanupMode, elementType, elementContext, extensionContext);
}
catch (Exception ex) {
throw new ExtensionConfigurationException("Failed to create default temp directory", ex);
Expand All @@ -285,8 +287,8 @@ static class CloseablePath implements CloseableResource {
private final CleanupMode cleanupMode;
private final ExtensionContext extensionContext;

private CloseablePath(TempDirFactory factory, CleanupMode cleanupMode, AnnotatedElementContext elementContext,
ExtensionContext extensionContext) throws Exception {
private CloseablePath(TempDirFactory factory, CleanupMode cleanupMode, Class<?> elementType,
AnnotatedElementContext elementContext, ExtensionContext extensionContext) throws Exception {
this.dir = factory.createTempDirectory(elementContext, extensionContext);
this.factory = factory;
this.cleanupMode = cleanupMode;
Expand All @@ -296,6 +298,13 @@ private CloseablePath(TempDirFactory factory, CleanupMode cleanupMode, Annotated
close();
throw new PreconditionViolationException("temp directory must be a directory");
}

if (elementType == File.class && !dir.getFileSystem().equals(FileSystems.getDefault())) {
close();
throw new PreconditionViolationException(
"temp directory with non-default file system cannot be injected into " + File.class.getName()
+ " target");
}
}

Path get() {
Expand Down
Loading

0 comments on commit 4dbd0f9

Please sign in to comment.