Skip to content
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 @@ -58,7 +58,8 @@ repository on GitHub.
[[release-notes-5.13.4-junit-jupiter-new-features-and-improvements]]
==== New Features and Improvements

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


[[release-notes-5.13.4-junit-vintage]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
abstract class AbstractExtensionContext<T extends TestDescriptor> implements ExtensionContextInternal, AutoCloseable {

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

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

this.valuesStore = createStore(parent, launcherStoreFacade, createCloseAction());
this.valuesStore = new NamespacedHierarchicalStore<>(getParentStore(parent), createCloseAction());
}

private NamespacedHierarchicalStore<org.junit.platform.engine.support.store.Namespace> getParentStore(
@Nullable ExtensionContext parent) {
return parent == null //
? this.launcherStoreFacade.getRequestLevelStore() //
: ((AbstractExtensionContext<?>) parent).valuesStore;
}

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

if (value instanceof @SuppressWarnings("resource") AutoCloseable closeable && isAutoCloseEnabled) {
if (isAutoCloseEnabled && value instanceof @SuppressWarnings("resource") AutoCloseable closeable) {
closeable.close();
return;
}

if (value instanceof Store.CloseableResource resource) {
if (isAutoCloseEnabled) {
LOGGER.warn(
() -> "Type implements CloseableResource but not AutoCloseable: " + value.getClass().getName());
store.computeIfAbsent(value.getClass(), type -> {
LOGGER.warn(() -> "Type implements CloseableResource but not AutoCloseable: " + type.getName());
return true;
});
}
resource.close();
}
};
}

private static NamespacedHierarchicalStore<org.junit.platform.engine.support.store.Namespace> createStore(
@Nullable ExtensionContext parent, LauncherStoreFacade launcherStoreFacade,
NamespacedHierarchicalStore.CloseAction<org.junit.platform.engine.support.store.Namespace> closeAction) {
NamespacedHierarchicalStore<org.junit.platform.engine.support.store.Namespace> parentStore;
if (parent == null) {
parentStore = launcherStoreFacade.getRequestLevelStore();
}
else {
parentStore = ((AbstractExtensionContext<?>) parent).valuesStore;
}
return new NamespacedHierarchicalStore<>(parentStore, closeAction);
}

@Override
public void close() {
this.valuesStore.close();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,19 +71,28 @@ void shouldNotCloseAutoCloseableWhenIsClosingStoredAutoCloseablesEnabledIsFalse(
void shouldLogWarningWhenResourceImplementsCloseableResourceButNotAutoCloseableAndConfigIsTrue(
@TrackLogRecords LogRecordListener listener) throws Exception {
ExecutionRecorder executionRecorder = new ExecutionRecorder();
CloseableResource resource = new CloseableResource();
String msg = "Type implements CloseableResource but not AutoCloseable: " + resource.getClass().getName();
CloseableResource resource1 = new CloseableResource();
CloseableResource resource2 = new CloseableResource();
CloseableResource resource3 = new CloseableResource() {
};
when(configuration.isClosingStoredAutoCloseablesEnabled()).thenReturn(true);

ExtensionContext extensionContext = new JupiterEngineExtensionContext(executionRecorder, testDescriptor,
configuration, extensionRegistry, launcherStoreFacade);
ExtensionContext.Store store = extensionContext.getStore(ExtensionContext.Namespace.GLOBAL);
store.put("resource", resource);
store.put("resource1", resource1);
store.put("resource2", resource2);
store.put("resource3", resource3);

((AutoCloseable) extensionContext).close();

assertThat(listener.stream(Level.WARNING)).map(LogRecord::getMessage).contains(msg);
assertThat(resource.closed).isTrue();
assertThat(listener.stream(Level.WARNING)).map(LogRecord::getMessage) //
.containsExactlyInAnyOrder(
"Type implements CloseableResource but not AutoCloseable: " + resource1.getClass().getName(),
"Type implements CloseableResource but not AutoCloseable: " + resource3.getClass().getName());
assertThat(resource1.closed).isTrue();
assertThat(resource2.closed).isTrue();
assertThat(resource3.closed).isTrue();
}

@Test
Expand Down
Loading