Skip to content

Commit 8e97a8b

Browse files
committed
Log only once per implementation type for CloseableResource types
To avoid flooding console output with warnings. Resolves #4776.
1 parent e6cc575 commit 8e97a8b

File tree

3 files changed

+33
-24
lines changed

3 files changed

+33
-24
lines changed

documentation/src/docs/asciidoc/release-notes/release-notes-5.13.4.adoc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ repository on GitHub.
5858
[[release-notes-5.13.4-junit-jupiter-new-features-and-improvements]]
5959
==== New Features and Improvements
6060

61-
* ❓
61+
* Log only once per implementation type for `CloseableResource` implementations that do
62+
not implement `AutoCloseable` to avoid flooding console output with this warning.
6263

6364

6465
[[release-notes-5.13.4-junit-vintage]]

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/AbstractExtensionContext.java

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@
5454
abstract class AbstractExtensionContext<T extends TestDescriptor> implements ExtensionContextInternal, AutoCloseable {
5555

5656
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractExtensionContext.class);
57+
private static final Namespace CLOSEABLE_RESOURCE_LOGGING_NAMESPACE = Namespace.create(
58+
AbstractExtensionContext.class, "CloseableResourceLogging");
5759

5860
private final @Nullable ExtensionContext parent;
5961
private final EngineExecutionListener engineExecutionListener;
@@ -86,42 +88,39 @@ abstract class AbstractExtensionContext<T extends TestDescriptor> implements Ext
8688
.collect(collectingAndThen(toCollection(LinkedHashSet::new), Collections::unmodifiableSet));
8789
// @formatter:on
8890

89-
this.valuesStore = createStore(parent, launcherStoreFacade, createCloseAction());
91+
this.valuesStore = new NamespacedHierarchicalStore<>(getParentStore(parent), createCloseAction());
92+
}
93+
94+
private NamespacedHierarchicalStore<org.junit.platform.engine.support.store.Namespace> getParentStore(
95+
@Nullable ExtensionContext parent) {
96+
return parent == null //
97+
? this.launcherStoreFacade.getRequestLevelStore() //
98+
: ((AbstractExtensionContext<?>) parent).valuesStore;
9099
}
91100

92101
@SuppressWarnings("deprecation")
93-
private NamespacedHierarchicalStore.CloseAction<org.junit.platform.engine.support.store.Namespace> createCloseAction() {
102+
private <N> NamespacedHierarchicalStore.CloseAction<N> createCloseAction() {
103+
var store = this.launcherStoreFacade.getSessionLevelStore(CLOSEABLE_RESOURCE_LOGGING_NAMESPACE);
94104
return (__, ___, value) -> {
95105
boolean isAutoCloseEnabled = this.configuration.isClosingStoredAutoCloseablesEnabled();
96106

97-
if (value instanceof @SuppressWarnings("resource") AutoCloseable closeable && isAutoCloseEnabled) {
107+
if (isAutoCloseEnabled && value instanceof @SuppressWarnings("resource") AutoCloseable closeable) {
98108
closeable.close();
99109
return;
100110
}
101111

102112
if (value instanceof Store.CloseableResource resource) {
103113
if (isAutoCloseEnabled) {
104-
LOGGER.warn(
105-
() -> "Type implements CloseableResource but not AutoCloseable: " + value.getClass().getName());
114+
store.computeIfAbsent(value.getClass(), type -> {
115+
LOGGER.warn(() -> "Type implements CloseableResource but not AutoCloseable: " + type.getName());
116+
return true;
117+
});
106118
}
107119
resource.close();
108120
}
109121
};
110122
}
111123

112-
private static NamespacedHierarchicalStore<org.junit.platform.engine.support.store.Namespace> createStore(
113-
@Nullable ExtensionContext parent, LauncherStoreFacade launcherStoreFacade,
114-
NamespacedHierarchicalStore.CloseAction<org.junit.platform.engine.support.store.Namespace> closeAction) {
115-
NamespacedHierarchicalStore<org.junit.platform.engine.support.store.Namespace> parentStore;
116-
if (parent == null) {
117-
parentStore = launcherStoreFacade.getRequestLevelStore();
118-
}
119-
else {
120-
parentStore = ((AbstractExtensionContext<?>) parent).valuesStore;
121-
}
122-
return new NamespacedHierarchicalStore<>(parentStore, closeAction);
123-
}
124-
125124
@Override
126125
public void close() {
127126
this.valuesStore.close();

jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/ResourceAutoClosingTests.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,19 +71,28 @@ void shouldNotCloseAutoCloseableWhenIsClosingStoredAutoCloseablesEnabledIsFalse(
7171
void shouldLogWarningWhenResourceImplementsCloseableResourceButNotAutoCloseableAndConfigIsTrue(
7272
@TrackLogRecords LogRecordListener listener) throws Exception {
7373
ExecutionRecorder executionRecorder = new ExecutionRecorder();
74-
CloseableResource resource = new CloseableResource();
75-
String msg = "Type implements CloseableResource but not AutoCloseable: " + resource.getClass().getName();
74+
CloseableResource resource1 = new CloseableResource();
75+
CloseableResource resource2 = new CloseableResource();
76+
CloseableResource resource3 = new CloseableResource() {
77+
};
7678
when(configuration.isClosingStoredAutoCloseablesEnabled()).thenReturn(true);
7779

7880
ExtensionContext extensionContext = new JupiterEngineExtensionContext(executionRecorder, testDescriptor,
7981
configuration, extensionRegistry, launcherStoreFacade);
8082
ExtensionContext.Store store = extensionContext.getStore(ExtensionContext.Namespace.GLOBAL);
81-
store.put("resource", resource);
83+
store.put("resource1", resource1);
84+
store.put("resource2", resource2);
85+
store.put("resource3", resource3);
8286

8387
((AutoCloseable) extensionContext).close();
8488

85-
assertThat(listener.stream(Level.WARNING)).map(LogRecord::getMessage).contains(msg);
86-
assertThat(resource.closed).isTrue();
89+
assertThat(listener.stream(Level.WARNING)).map(LogRecord::getMessage) //
90+
.containsExactlyInAnyOrder(
91+
"Type implements CloseableResource but not AutoCloseable: " + resource1.getClass().getName(),
92+
"Type implements CloseableResource but not AutoCloseable: " + resource3.getClass().getName());
93+
assertThat(resource1.closed).isTrue();
94+
assertThat(resource2.closed).isTrue();
95+
assertThat(resource3.closed).isTrue();
8796
}
8897

8998
@Test

0 commit comments

Comments
 (0)