Skip to content

Commit 0211454

Browse files
committed
Include AnnotatedElement description in @⁠TempDir log messages
Prior to this commit, log messages for @⁠TempDir retention looked similar to the following. INFO: Skipping cleanup of temp dir <path> due to cleanup mode configuration. However, the lack of information about which @⁠TempDir declaration was affected did not provide sufficient information to users. To improve diagnostics, log messages for @⁠TempDir retention now include the configured CleanupMode and describe the AnnotatedElement (field, constructor parameter, or method parameter), as in the following examples. Field: INFO: Skipping cleanup of temp dir <path> for field TestCase.tempDir due to CleanupMode.NEVER. Constructor Parameter: INFO: Skipping cleanup of temp dir <path> for parameter 'tempDir' in constructor TestCase(TestInfo, Path) due to CleanupMode.ON_SUCCESS. Method Parameter: INFO: Skipping cleanup of temp dir <path> for parameter 'tempDir' in method test(TestInfo, Path) due to CleanupMode.ON_SUCCESS. Closes: #4282
1 parent c9438d5 commit 0211454

File tree

2 files changed

+111
-6
lines changed

2 files changed

+111
-6
lines changed

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

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import java.io.File;
2525
import java.io.IOException;
2626
import java.lang.reflect.AnnotatedElement;
27+
import java.lang.reflect.Constructor;
28+
import java.lang.reflect.Executable;
2729
import java.lang.reflect.Field;
2830
import java.lang.reflect.Parameter;
2931
import java.nio.file.DirectoryNotEmptyException;
@@ -63,6 +65,7 @@
6365
import org.junit.platform.commons.logging.LoggerFactory;
6466
import org.junit.platform.commons.support.ModifierSupport;
6567
import org.junit.platform.commons.support.ReflectionSupport;
68+
import org.junit.platform.commons.util.ClassUtils;
6669
import org.junit.platform.commons.util.ExceptionUtils;
6770
import org.junit.platform.commons.util.Preconditions;
6871
import org.junit.platform.commons.util.ToStringBuilder;
@@ -288,13 +291,15 @@ static class CloseablePath implements CloseableResource {
288291
private final Path dir;
289292
private final TempDirFactory factory;
290293
private final CleanupMode cleanupMode;
294+
private final AnnotatedElement annotatedElement;
291295
private final ExtensionContext extensionContext;
292296

293297
private CloseablePath(TempDirFactory factory, CleanupMode cleanupMode, Class<?> elementType,
294298
AnnotatedElementContext elementContext, ExtensionContext extensionContext) throws Exception {
295299
this.dir = factory.createTempDirectory(elementContext, extensionContext);
296300
this.factory = factory;
297301
this.cleanupMode = cleanupMode;
302+
this.annotatedElement = elementContext.getAnnotatedElement();
298303
this.extensionContext = extensionContext;
299304

300305
if (dir == null || !Files.isDirectory(dir)) {
@@ -318,7 +323,8 @@ Path get() {
318323
public void close() throws IOException {
319324
try {
320325
if (cleanupMode == NEVER || (cleanupMode == ON_SUCCESS && selfOrChildFailed(extensionContext))) {
321-
logger.info(() -> "Skipping cleanup of temp dir " + dir + " due to cleanup mode configuration.");
326+
logger.info(() -> String.format("Skipping cleanup of temp dir %s for %s due to CleanupMode.%s.",
327+
dir, descriptionFor(annotatedElement), cleanupMode.name()));
322328
return;
323329
}
324330

@@ -335,6 +341,33 @@ public void close() throws IOException {
335341
}
336342
}
337343

344+
/**
345+
* @since 5.12
346+
*/
347+
private static String descriptionFor(AnnotatedElement annotatedElement) {
348+
if (annotatedElement instanceof Field) {
349+
Field field = (Field) annotatedElement;
350+
return "field " + field.getDeclaringClass().getSimpleName() + "." + field.getName();
351+
}
352+
if (annotatedElement instanceof Parameter) {
353+
Parameter parameter = (Parameter) annotatedElement;
354+
Executable executable = parameter.getDeclaringExecutable();
355+
return "parameter '" + parameter.getName() + "' in " + descriptionFor(executable);
356+
}
357+
throw new IllegalStateException("Unsupported AnnotatedElement type for @TempDir: " + annotatedElement);
358+
}
359+
360+
/**
361+
* @since 5.12
362+
*/
363+
private static String descriptionFor(Executable executable) {
364+
boolean isConstructor = executable instanceof Constructor<?>;
365+
String type = isConstructor ? "constructor" : "method";
366+
String name = isConstructor ? executable.getDeclaringClass().getSimpleName() : executable.getName();
367+
return String.format("%s %s(%s)", type, name,
368+
ClassUtils.nullSafeToString(Class::getSimpleName, executable.getParameterTypes()));
369+
}
370+
338371
private SortedMap<Path, IOException> deleteAllFilesAndDirectories(FileOperations fileOperations)
339372
throws IOException {
340373
if (dir == null || Files.notExists(dir)) {

jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/CloseablePathTests.java

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,16 @@
3737
import java.io.IOException;
3838
import java.lang.annotation.Retention;
3939
import java.lang.annotation.Target;
40+
import java.lang.reflect.AnnotatedElement;
41+
import java.lang.reflect.Constructor;
42+
import java.lang.reflect.Field;
43+
import java.lang.reflect.Method;
44+
import java.lang.reflect.Parameter;
4045
import java.nio.file.FileSystem;
4146
import java.nio.file.Path;
4247
import java.util.Optional;
48+
import java.util.logging.Level;
49+
import java.util.logging.LogRecord;
4350

4451
import com.google.common.jimfs.Jimfs;
4552

@@ -49,11 +56,13 @@
4956
import org.junit.jupiter.api.DisplayName;
5057
import org.junit.jupiter.api.Nested;
5158
import org.junit.jupiter.api.Test;
59+
import org.junit.jupiter.api.TestInfo;
5260
import org.junit.jupiter.api.condition.DisabledOnOs;
5361
import org.junit.jupiter.api.extension.AnnotatedElementContext;
5462
import org.junit.jupiter.api.extension.ExtensionConfigurationException;
5563
import org.junit.jupiter.api.extension.ExtensionContext;
5664
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
65+
import org.junit.jupiter.api.fixtures.TrackLogRecords;
5766
import org.junit.jupiter.api.io.CleanupMode;
5867
import org.junit.jupiter.api.io.TempDir;
5968
import org.junit.jupiter.api.io.TempDirFactory;
@@ -62,6 +71,7 @@
6271
import org.junit.jupiter.params.ParameterizedTest;
6372
import org.junit.jupiter.params.provider.ValueSource;
6473
import org.junit.platform.commons.PreconditionViolationException;
74+
import org.junit.platform.commons.logging.LogRecordListener;
6575
import org.junit.platform.engine.support.store.NamespacedHierarchicalStore;
6676

6777
/**
@@ -273,7 +283,7 @@ void cleanupTempDirectory() throws IOException {
273283
@DisplayName("is done for a cleanup mode of ALWAYS")
274284
@ParameterizedTest
275285
@ElementTypeSource
276-
void always(Class<?> elementType) throws IOException {
286+
void always(Class<?> elementType, @TrackLogRecords LogRecordListener listener) throws IOException {
277287
reset(factory);
278288

279289
closeablePath = TempDirectory.createTempDir(factory, ALWAYS, elementType, elementContext, extensionContext);
@@ -283,30 +293,75 @@ void always(Class<?> elementType) throws IOException {
283293

284294
verify(factory).close();
285295
assertThat(closeablePath.get()).doesNotExist();
296+
assertThat(listener.stream(Level.INFO)).map(LogRecord::getMessage)//
297+
.noneMatch(m -> m.startsWith("Skipping cleanup of temp dir"));
286298
}
287299

288300
@DisplayName("is not done for a cleanup mode of NEVER")
289301
@ParameterizedTest
290302
@ElementTypeSource
291-
void never(Class<?> elementType) throws IOException {
303+
void never(Class<?> elementType, @TrackLogRecords LogRecordListener listener) throws Exception {
292304
reset(factory);
293305

306+
when(elementContext.getAnnotatedElement()).thenReturn(TestCase.class.getDeclaredField("tempDir"));
307+
294308
closeablePath = TempDirectory.createTempDir(factory, NEVER, elementType, elementContext, extensionContext);
295309
assertThat(closeablePath.get()).isDirectory();
296310

297311
closeablePath.close();
298312

299313
verify(factory).close();
300314
assertThat(closeablePath.get()).exists();
315+
assertThat(listener.stream(Level.INFO)).map(LogRecord::getMessage)//
316+
.anyMatch(m -> m.startsWith("Skipping cleanup of temp dir ")
317+
&& m.endsWith(" for field TestCase.tempDir due to CleanupMode.NEVER."));
318+
}
319+
320+
@DisplayName("is not done for a cleanup mode of ON_SUCCESS, if there is an exception (for annotated field)")
321+
@ParameterizedTest
322+
@ElementTypeSource
323+
void onSuccessWithExceptionForAnnotatedField(Class<?> elementType, @TrackLogRecords LogRecordListener listener)
324+
throws Exception {
325+
326+
Field field = TestCase.class.getDeclaredField("tempDir");
327+
328+
onSuccessWithException(elementType, listener, field,
329+
" for field TestCase.tempDir due to CleanupMode.ON_SUCCESS.");
330+
}
331+
332+
@DisplayName("is not done for a cleanup mode of ON_SUCCESS, if there is an exception (for annotated method parameter)")
333+
@ParameterizedTest
334+
@ElementTypeSource
335+
void onSuccessWithExceptionForAnnotatedMethodParameter(Class<?> elementType,
336+
@TrackLogRecords LogRecordListener listener) throws Exception {
337+
338+
Method method = TestCase.class.getDeclaredMethod("test", TestInfo.class, Path.class);
339+
Parameter parameter = method.getParameters()[1];
340+
341+
onSuccessWithException(elementType, listener, parameter,
342+
"for parameter 'tempDir' in method test(TestInfo, Path) due to CleanupMode.ON_SUCCESS.");
301343
}
302344

303-
@DisplayName("is not done for a cleanup mode of ON_SUCCESS, if there is an exception")
345+
@DisplayName("is not done for a cleanup mode of ON_SUCCESS, if there is an exception (for annotated constructor parameter)")
304346
@ParameterizedTest
305347
@ElementTypeSource
306-
void onSuccessWithException(Class<?> elementType) throws IOException {
348+
void onSuccessWithExceptionForAnnotatedConstructorParameter(Class<?> elementType,
349+
@TrackLogRecords LogRecordListener listener) throws Exception {
350+
351+
Constructor<?> constructor = TestCase.class.getDeclaredConstructor(TestInfo.class, Path.class);
352+
Parameter parameter = constructor.getParameters()[1];
353+
354+
onSuccessWithException(elementType, listener, parameter,
355+
"for parameter 'tempDir' in constructor TestCase(TestInfo, Path) due to CleanupMode.ON_SUCCESS.");
356+
}
357+
358+
private void onSuccessWithException(Class<?> elementType, @TrackLogRecords LogRecordListener listener,
359+
AnnotatedElement annotatedElement, String expectedMessage) throws Exception {
360+
307361
reset(factory);
308362

309363
when(extensionContext.getExecutionException()).thenReturn(Optional.of(new Exception()));
364+
when(elementContext.getAnnotatedElement()).thenReturn(annotatedElement);
310365

311366
closeablePath = TempDirectory.createTempDir(factory, ON_SUCCESS, elementType, elementContext,
312367
extensionContext);
@@ -316,12 +371,16 @@ void onSuccessWithException(Class<?> elementType) throws IOException {
316371

317372
verify(factory).close();
318373
assertThat(closeablePath.get()).exists();
374+
assertThat(listener.stream(Level.INFO)).map(LogRecord::getMessage)//
375+
.anyMatch(m -> m.startsWith("Skipping cleanup of temp dir ") && m.endsWith(expectedMessage));
319376
}
320377

321378
@DisplayName("is done for a cleanup mode of ON_SUCCESS, if there is no exception")
322379
@ParameterizedTest
323380
@ElementTypeSource
324-
void onSuccessWithNoException(Class<?> elementType) throws IOException {
381+
void onSuccessWithNoException(Class<?> elementType, @TrackLogRecords LogRecordListener listener)
382+
throws IOException {
383+
325384
reset(factory);
326385

327386
when(extensionContext.getExecutionException()).thenReturn(Optional.empty());
@@ -334,8 +393,21 @@ void onSuccessWithNoException(Class<?> elementType) throws IOException {
334393

335394
verify(factory).close();
336395
assertThat(closeablePath.get()).doesNotExist();
396+
assertThat(listener.stream(Level.INFO)).map(LogRecord::getMessage)//
397+
.noneMatch(m -> m.startsWith("Skipping cleanup of temp dir"));
337398
}
338399

339400
}
340401

402+
static class TestCase {
403+
404+
Path tempDir;
405+
406+
TestCase(TestInfo testInfo, Path tempDir) {
407+
}
408+
409+
void test(TestInfo testInfo, Path tempDir) {
410+
}
411+
}
412+
341413
}

0 commit comments

Comments
 (0)