From 15913882b138bbb6ab1885173ea19b123c63bde0 Mon Sep 17 00:00:00 2001 From: Stefano Cordio <stefano_cordio@epam.com> Date: Sat, 10 Aug 2024 11:30:44 +0200 Subject: [PATCH 1/4] Let `@TempDir` fail fast with `File` annotated element and non-default file system temp directory --- .../release-notes/release-notes-5.11.0.adoc | 3 + .../engine/extension/TempDirectory.java | 27 ++-- .../engine/extension/CloseablePathTests.java | 145 ++++++++++++++---- .../TempDirectoryPerDeclarationTests.java | 49 +++++- 4 files changed, 183 insertions(+), 41 deletions(-) diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.0.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.0.adoc index 56c448b69304..08edb8126cd6 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.0.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.0.adoc @@ -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]] 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 a4bb8662cb4c..ac5831d88329 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 @@ -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; @@ -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); @@ -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) { @@ -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); @@ -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; @@ -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() { diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/CloseablePathTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/CloseablePathTests.java index 3f76a49c77b0..f637e7f6e50d 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/CloseablePathTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/CloseablePathTests.java @@ -10,6 +10,9 @@ package org.junit.jupiter.engine.extension; +import static com.google.common.jimfs.Configuration.unix; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.nio.file.Files.createDirectory; import static java.nio.file.Files.createFile; import static java.nio.file.Files.createSymbolicLink; @@ -18,20 +21,28 @@ import static java.nio.file.Files.deleteIfExists; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.junit.jupiter.api.condition.OS.WINDOWS; import static org.junit.jupiter.api.io.CleanupMode.ALWAYS; import static org.junit.jupiter.api.io.CleanupMode.DEFAULT; import static org.junit.jupiter.api.io.CleanupMode.NEVER; import static org.junit.jupiter.api.io.CleanupMode.ON_SUCCESS; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.io.File; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.nio.file.FileSystem; import java.nio.file.Path; import java.util.Optional; +import com.google.common.jimfs.Jimfs; + import org.assertj.core.api.ThrowableAssert.ThrowingCallable; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -39,7 +50,6 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; -import org.junit.jupiter.api.condition.OS; import org.junit.jupiter.api.extension.AnnotatedElementContext; import org.junit.jupiter.api.extension.ExtensionConfigurationException; import org.junit.jupiter.api.extension.ExtensionContext; @@ -49,6 +59,8 @@ import org.junit.jupiter.api.io.TempDirFactory; import org.junit.jupiter.engine.AbstractJupiterTestEngineTests; import org.junit.jupiter.engine.execution.NamespaceAwareStore; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.junit.platform.commons.PreconditionViolationException; import org.junit.platform.engine.support.store.NamespacedHierarchicalStore; @@ -65,6 +77,12 @@ class CloseablePathTests extends AbstractJupiterTestEngineTests { private TempDirectory.CloseablePath closeablePath; + @Target(METHOD) + @Retention(RUNTIME) + @ValueSource(classes = { File.class, Path.class }) + private @interface ElementTypeSource { + } + @BeforeEach void setUpExtensionContext() { var store = new NamespaceAwareStore(new NamespacedHierarchicalStore<>(null), Namespace.GLOBAL); @@ -95,65 +113,84 @@ void cleanupRoot() throws IOException { delete(root); } - @Test @DisplayName("succeeds if the factory returns a directory") - void factoryReturnsDirectory() throws Exception { - TempDirFactory factory = spy(new Factory(createDirectory(root.resolve("directory")))); + @ParameterizedTest + @ElementTypeSource + void factoryReturnsDirectoryDynamic(Class<?> elementType) throws IOException { + TempDirFactory factory = (elementContext, extensionContext) -> createDirectory(root.resolve("directory")); - closeablePath = TempDirectory.createTempDir(factory, DEFAULT, elementContext, extensionContext); + closeablePath = TempDirectory.createTempDir(factory, DEFAULT, elementType, elementContext, + extensionContext); assertThat(closeablePath.get()).isDirectory(); delete(closeablePath.get()); } - @Test @DisplayName("succeeds if the factory returns a symbolic link to a directory") - @DisabledOnOs(OS.WINDOWS) - void factoryReturnsSymbolicLinkToDirectory() throws Exception { + @ParameterizedTest + @ElementTypeSource + @DisabledOnOs(WINDOWS) + void factoryReturnsSymbolicLinkToDirectory(Class<?> elementType) throws IOException { Path directory = createDirectory(root.resolve("directory")); - TempDirFactory factory = spy(new Factory(createSymbolicLink(root.resolve("symbolicLink"), directory))); + TempDirFactory factory = (elementContext, + extensionContext) -> createSymbolicLink(root.resolve("symbolicLink"), directory); - closeablePath = TempDirectory.createTempDir(factory, DEFAULT, elementContext, extensionContext); + closeablePath = TempDirectory.createTempDir(factory, DEFAULT, elementType, elementContext, + extensionContext); assertThat(closeablePath.get()).isDirectory(); delete(closeablePath.get()); delete(directory); } + @DisplayName("succeeds if the factory returns a directory on a non-default file system for a Path annotated element") @Test + void factoryReturnsDirectoryOnNonDefaultFileSystemWithPath() throws IOException { + TempDirFactory factory = spy(new JimfsFactory()); + + closeablePath = TempDirectory.createTempDir(factory, DEFAULT, Path.class, elementContext, extensionContext); + assertThat(closeablePath.get()).isDirectory(); + + delete(closeablePath.get()); + } + @DisplayName("fails if the factory returns null") - void factoryReturnsNull() throws IOException { + @ParameterizedTest + @ElementTypeSource + void factoryReturnsNull(Class<?> elementType) throws IOException { TempDirFactory factory = spy(new Factory(null)); assertThatExtensionConfigurationExceptionIsThrownBy( - () -> TempDirectory.createTempDir(factory, DEFAULT, elementContext, extensionContext)); + () -> TempDirectory.createTempDir(factory, DEFAULT, elementType, elementContext, extensionContext)); verify(factory).close(); } - @Test @DisplayName("fails if the factory returns a file") - void factoryReturnsFile() throws IOException { + @ParameterizedTest + @ElementTypeSource + void factoryReturnsFile(Class<?> elementType) throws IOException { Path file = createFile(root.resolve("file")); TempDirFactory factory = spy(new Factory(file)); assertThatExtensionConfigurationExceptionIsThrownBy( - () -> TempDirectory.createTempDir(factory, DEFAULT, elementContext, extensionContext)); + () -> TempDirectory.createTempDir(factory, DEFAULT, elementType, elementContext, extensionContext)); verify(factory).close(); assertThat(file).doesNotExist(); } - @Test @DisplayName("fails if the factory returns a symbolic link to a file") - @DisabledOnOs(OS.WINDOWS) - void factoryReturnsSymbolicLinkToFile() throws IOException { + @ParameterizedTest + @ElementTypeSource + @DisabledOnOs(WINDOWS) + void factoryReturnsSymbolicLinkToFile(Class<?> elementType) throws IOException { Path file = createFile(root.resolve("file")); Path symbolicLink = createSymbolicLink(root.resolve("symbolicLink"), file); TempDirFactory factory = spy(new Factory(symbolicLink)); assertThatExtensionConfigurationExceptionIsThrownBy( - () -> TempDirectory.createTempDir(factory, DEFAULT, elementContext, extensionContext)); + () -> TempDirectory.createTempDir(factory, DEFAULT, elementType, elementContext, extensionContext)); verify(factory).close(); assertThat(symbolicLink).doesNotExist(); @@ -161,6 +198,22 @@ void factoryReturnsSymbolicLinkToFile() throws IOException { delete(file); } + @DisplayName("fails if the factory returns a directory on a non-default file system for a File annotated element") + @Test + void factoryReturnsDirectoryOnNonDefaultFileSystemWithFile() throws IOException { + TempDirFactory factory = spy(new JimfsFactory()); + + assertThatExceptionOfType(ExtensionConfigurationException.class)// + .isThrownBy(() -> TempDirectory.createTempDir(factory, DEFAULT, File.class, elementContext, + extensionContext))// + .withMessage("Failed to create default temp directory")// + .withCauseInstanceOf(PreconditionViolationException.class)// + .havingCause().withMessage("temp directory with non-default file system cannot be injected into " + + File.class.getName() + " target"); + + verify(factory).close(); + } + // Mockito spying a lambda fails with: VM does not support modification of given type private record Factory(Path path) implements TempDirFactory { @@ -171,6 +224,22 @@ public Path createTempDirectory(AnnotatedElementContext elementContext, Extensio } + private static class JimfsFactory implements TempDirFactory { + + private final FileSystem fileSystem = Jimfs.newFileSystem(unix()); + + @Override + public Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext) + throws Exception { + return createDirectory(fileSystem.getPath("/").resolve("directory")); + } + + @Override + public void close() throws IOException { + TempDirFactory.super.close(); + } + } + private static void assertThatExtensionConfigurationExceptionIsThrownBy(ThrowingCallable callable) { assertThatExceptionOfType(ExtensionConfigurationException.class)// .isThrownBy(callable)// @@ -201,10 +270,13 @@ void cleanupTempDirectory() throws IOException { deleteIfExists(closeablePath.get()); } - @Test @DisplayName("is done for a cleanup mode of ALWAYS") - void always() throws IOException { - closeablePath = TempDirectory.createTempDir(factory, ALWAYS, elementContext, extensionContext); + @ParameterizedTest + @ElementTypeSource + void always(Class<?> elementType) throws IOException { + reset(factory); + + closeablePath = TempDirectory.createTempDir(factory, ALWAYS, elementType, elementContext, extensionContext); assertThat(closeablePath.get()).isDirectory(); closeablePath.close(); @@ -213,10 +285,13 @@ void always() throws IOException { assertThat(closeablePath.get()).doesNotExist(); } - @Test @DisplayName("is not done for a cleanup mode of NEVER") - void never() throws IOException { - closeablePath = TempDirectory.createTempDir(factory, NEVER, elementContext, extensionContext); + @ParameterizedTest + @ElementTypeSource + void never(Class<?> elementType) throws IOException { + reset(factory); + + closeablePath = TempDirectory.createTempDir(factory, NEVER, elementType, elementContext, extensionContext); assertThat(closeablePath.get()).isDirectory(); closeablePath.close(); @@ -225,12 +300,16 @@ void never() throws IOException { assertThat(closeablePath.get()).exists(); } - @Test @DisplayName("is not done for a cleanup mode of ON_SUCCESS, if there is an exception") - void onSuccessWithException() throws IOException { + @ParameterizedTest + @ElementTypeSource + void onSuccessWithException(Class<?> elementType) throws IOException { + reset(factory); + when(extensionContext.getExecutionException()).thenReturn(Optional.of(new Exception())); - closeablePath = TempDirectory.createTempDir(factory, ON_SUCCESS, elementContext, extensionContext); + closeablePath = TempDirectory.createTempDir(factory, ON_SUCCESS, elementType, elementContext, + extensionContext); assertThat(closeablePath.get()).isDirectory(); closeablePath.close(); @@ -239,12 +318,16 @@ void onSuccessWithException() throws IOException { assertThat(closeablePath.get()).exists(); } - @Test @DisplayName("is done for a cleanup mode of ON_SUCCESS, if there is no exception") - void onSuccessWithNoException() throws IOException { + @ParameterizedTest + @ElementTypeSource + void onSuccessWithNoException(Class<?> elementType) throws IOException { + reset(factory); + when(extensionContext.getExecutionException()).thenReturn(Optional.empty()); - closeablePath = TempDirectory.createTempDir(factory, ON_SUCCESS, elementContext, extensionContext); + closeablePath = TempDirectory.createTempDir(factory, ON_SUCCESS, elementType, elementContext, + extensionContext); assertThat(closeablePath.get()).isDirectory(); closeablePath.close(); diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerDeclarationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerDeclarationTests.java index 8c9e455e0041..cfd8fbc452af 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerDeclarationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerDeclarationTests.java @@ -321,7 +321,7 @@ void doesNotSupportTempDirFactoryNotReturningDirectory() { @Test @DisplayName("when default @TempDir factory does not return directory") @Order(33) - void doesNotSupportCustomDefaultTempDirFactoryReturningNull() { + void doesNotSupportCustomDefaultTempDirFactoryNotReturningDirectory() { var results = executeTestsForClassWithDefaultFactory( CustomDefaultFactoryNotReturningDirectoryTestCase.class, FactoryNotReturningDirectory.class); @@ -347,6 +347,28 @@ public Path createTempDirectory(AnnotatedElementContext elementContext, Extensio } } + @Test + @DisplayName("when @TempDir factory returns a non-default file system path for a File annotated element") + @Order(34) + void doesNotSupportNonDefaultFileSystemTempDirFactoryOnFileAnnotatedElement() { + var results = executeTestsForClass( + FactoryReturningNonDefaultFileSystemPathForFileAnnotatedElementTestCase.class); + + // @formatter:off + assertSingleFailedTest(results, instanceOf(ParameterResolutionException.class), + message(m -> m.matches("Failed to resolve parameter \\[.+] in method \\[.+]: .+")), + cause( + instanceOf(ExtensionConfigurationException.class), + message("Failed to create default temp directory"), + cause( + instanceOf(PreconditionViolationException.class), + message("temp directory with non-default file system cannot be injected into " + + File.class.getName() + " target") + ) + )); + // @formatter:on + } + } @Nested @@ -1424,6 +1446,31 @@ public Path createTempDirectory(AnnotatedElementContext elementContext, Extensio } + static class FactoryReturningNonDefaultFileSystemPathForFileAnnotatedElementTestCase { + + @Test + void test(@SuppressWarnings("unused") @TempDir(factory = Factory.class) File tempDir) { + // never called + } + + private static class Factory implements TempDirFactory { + + private final FileSystem fileSystem = Jimfs.newFileSystem(Configuration.unix()); + + @Override + public Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext) + throws Exception { + return Files.createTempDirectory(fileSystem.getPath("/"), "prefix"); + } + + @Override + public void close() throws IOException { + fileSystem.close(); + } + } + + } + static class StandardDefaultFactoryTestCase { @Test From 38744ab6d2763b1147fa13829027a3284e9fb605 Mon Sep 17 00:00:00 2001 From: Stefano Cordio <stefano_cordio@epam.com> Date: Mon, 12 Aug 2024 13:19:06 +0200 Subject: [PATCH 2/4] Remove unnecessary spy --- .../org/junit/jupiter/engine/extension/CloseablePathTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/CloseablePathTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/CloseablePathTests.java index f637e7f6e50d..6a0645975813 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/CloseablePathTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/CloseablePathTests.java @@ -146,7 +146,7 @@ void factoryReturnsSymbolicLinkToDirectory(Class<?> elementType) throws IOExcept @DisplayName("succeeds if the factory returns a directory on a non-default file system for a Path annotated element") @Test void factoryReturnsDirectoryOnNonDefaultFileSystemWithPath() throws IOException { - TempDirFactory factory = spy(new JimfsFactory()); + TempDirFactory factory = new JimfsFactory(); closeablePath = TempDirectory.createTempDir(factory, DEFAULT, Path.class, elementContext, extensionContext); assertThat(closeablePath.get()).isDirectory(); From 37df8415b522448a4e9155fe156f73d1572c8e9c Mon Sep 17 00:00:00 2001 From: Stefano Cordio <stefano_cordio@epam.com> Date: Mon, 12 Aug 2024 14:58:13 +0200 Subject: [PATCH 3/4] Update `TempDir` Javadoc --- .../org/junit/jupiter/api/io/TempDir.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/TempDir.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/TempDir.java index de5fd182d6a8..c72fa297c55e 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/TempDir.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/TempDir.java @@ -40,11 +40,20 @@ * * <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 + * {@link TempDir#factory() factory} is used, which creates a temporary directory that does + * not belong to the {@link 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> From 99f8f1e0bf667c4cea722fd6903ad992d13cbd80 Mon Sep 17 00:00:00 2001 From: Marc Philipp <mail@marcphilipp.de> Date: Mon, 12 Aug 2024 15:34:33 +0200 Subject: [PATCH 4/4] Polisj Javadoc Use linkplain rather than link --- .../java/org/junit/jupiter/api/io/TempDir.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/TempDir.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/TempDir.java index c72fa297c55e..55d9bbfcf56b 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/TempDir.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/TempDir.java @@ -41,16 +41,20 @@ * <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}. - * An {@link ExtensionConfigurationException} or a {@link ParameterResolutionException} - * will be thrown in one of the following cases: + * 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 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 - * {@link TempDir#factory() factory} is used, which creates a temporary directory that does - * not belong to the {@link java.nio.file.FileSystems#getDefault() default file system}.</li> + * {@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