Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Let @TempDir fail fast with File annotated element and non-default file system temp directory #3921

Merged
merged 4 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading