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

Introduce feature flag for auto-closing AutoCloseable in Jupiter's ExtensionContext.Store #4452

Open
wants to merge 16 commits into
base: main
Choose a base branch
from

Conversation

YongGoose
Copy link
Contributor

@YongGoose YongGoose commented Apr 6, 2025

Overview

fix #4434


I hereby agree to the terms of the JUnit Contributor License Agreement.


Definition of Done

((CloseableResource) value).close();
}
};
private static NamespacedHierarchicalStore.CloseAction<org.junit.platform.engine.support.store.Namespace> CLOSE_RESOURCES;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should no longer be static since it depends on the instance-specific JupiterConfiguration.

@YongGoose YongGoose marked this pull request as ready for review April 9, 2025 14:18
@YongGoose YongGoose requested a review from marcphilipp April 9, 2025 14:19
Comment on lines 921 to 929
To determine if your extension is running under JUnit Jupiter 5.13+, you can check if auto-close is enabled via the configuration:

[source,java,indent=0]
----
boolean isJupiter513OrNewer = context.getConfigurationParameter("junit.jupiter.extensions.autoclose.enabled")
.map(Boolean::parseBoolean)
.orElse(true); // Default is true in 5.13+
----

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd omit that as it's not reliable since the config param could be set on earlier versions.

@@ -65,7 +64,7 @@ public interface TestInstantiationAwareExtension extends Extension {
* <li>{@link ExtensionContext#getTestMethod() getTestMethod()} is no longer
* empty, unless the {@link TestInstance.Lifecycle#PER_CLASS PER_CLASS}
* lifecycle is used.</li>
* <li>If the callback adds a new {@link CloseableResource} to the
* <li>If the callback adds a new {@link Store.CloseableResource} to the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also mention AutoCloseable here as in the other places you've adapted the Javadoc.

@@ -42,14 +42,15 @@ public interface JupiterConfiguration {
String EXTENSIONS_AUTODETECTION_EXCLUDE_PROPERTY_NAME = "junit.jupiter.extensions.autodetection.exclude";
String DEACTIVATE_CONDITIONS_PATTERN_PROPERTY_NAME = "junit.jupiter.conditions.deactivate";
String PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME = "junit.jupiter.execution.parallel.enabled";
String AUTOCLOSE_ENABLED_PROPERTY_NAME = "junit.jupiter.extensions.autoclose.enabled";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about the name. I'll discuss it with the team.

Suggested change
String AUTOCLOSE_ENABLED_PROPERTY_NAME = "junit.jupiter.extensions.autoclose.enabled";
String CLOSING_STORED_AUTO_CLOSEABLE_ENABLED_PROPERTY_NAME = "junit.jupiter.extensions.store.close.autocloseable.enabled";

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO, I think it's a good name.
Please discuss it with the team and let me know the outcome!

@@ -60,6 +61,8 @@ public interface JupiterConfiguration {

boolean isParallelExecutionEnabled();

boolean isAutoCloseEnabled();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔

Suggested change
boolean isAutoCloseEnabled();
boolean isClosingStoredAutoCloseablesEnabled();

@@ -289,7 +288,7 @@ private static boolean selfOrChildFailed(ExtensionContext context) {
|| context.getStore(NAMESPACE).getOrDefault(CHILD_FAILED, Boolean.class, false);
}

static class CloseablePath implements CloseableResource {
static class CloseablePath implements AutoCloseable {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to implement both here in case the new config param is set to false (the same goes for all implementations of CloseableResource in production code).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to implement both here in case the new config param is set to false (the same goes for all implementations of CloseableResource in production code).

I overlooked the case where the config param could be set to false.
I've updated the code to implement both, as you suggested. 🙂

Thank you @marcphilipp !!

@@ -129,7 +128,7 @@ public void beforeEach(ExtensionContext context) {
}

private static void installFailureTracker(ExtensionContext context) {
context.getStore(NAMESPACE).put(FAILURE_TRACKER, (CloseableResource) () -> context.getParent() //
context.getStore(NAMESPACE).put(FAILURE_TRACKER, (AutoCloseable) () -> context.getParent() //
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should extract a class here that also implements both.

@@ -76,6 +76,7 @@ private void storeParameterInfo(ExtensionContext context) {
new DefaultParameterInfo(declarations, accessor).store(context);
}

@SuppressWarnings("deprecation")
private static class CloseableArgument implements ExtensionContext.Store.CloseableResource {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should also implement both

@@ -80,7 +79,7 @@ static class ThrowingOnCloseExtension implements BeforeEachCallback {

@Override
public void beforeEach(ExtensionContext context) {
context.getStore(GLOBAL).put("throwingResource", (ExtensionContext.Store.CloseableResource) () -> {
context.getStore(GLOBAL).put("throwingResource", (AutoCloseable) () -> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto

@@ -1175,7 +1174,7 @@ public Object resolveParameter(ParameterContext parameterContext, ExtensionConte
}
}

static class SomeResource implements CloseableResource {
static class SomeResource implements AutoCloseable {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine in test code. 👍

Comment on lines 600 to 602
* <p>If {@code type} implements {@link ExtensionContext.Store.CloseableResource}
* the {@code close()} method will be invoked on the stored object when
* or {@link AutoCloseable} the {@code close()} method will be invoked on the stored object when
* the store is closed.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

		 * <p>If {@code type} implements {@link CloseableResource} or
		 * {@link AutoCloseable} (unless the
		 * {@code junit.jupiter.extensions.store.close.autocloseable.enabled}
		 * configuration parameter is set to {@code false}), then the {@code close()}
		 * method will be invoked on the stored object when the store is closed.

private final ExecutableInvoker executableInvoker;
private final ExtensionRegistry extensionRegistry;
private final LauncherStoreFacade launcherStoreFacade;
private final NamespacedHierarchicalStore.CloseAction<org.junit.platform.engine.support.store.Namespace> closeResources;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no need to store this in a field since it's only used in the constructor, isn't it?

Suggested change
private final NamespacedHierarchicalStore.CloseAction<org.junit.platform.engine.support.store.Namespace> closeResources;

Comment on lines +89 to +90
this.closeResources = createCloseResources();
this.valuesStore = createStore(parent, launcherStoreFacade);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
this.closeResources = createCloseResources();
this.valuesStore = createStore(parent, launcherStoreFacade);
this.valuesStore = createStore(parent, launcherStoreFacade, createCloseAction());

}

@SuppressWarnings("deprecation")
private NamespacedHierarchicalStore.CloseAction<org.junit.platform.engine.support.store.Namespace> createCloseResources() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private NamespacedHierarchicalStore.CloseAction<org.junit.platform.engine.support.store.Namespace> createCloseResources() {
private NamespacedHierarchicalStore.CloseAction<org.junit.platform.engine.support.store.Namespace> createCloseAction() {

}

private static NamespacedHierarchicalStore<org.junit.platform.engine.support.store.Namespace> createStore(
private NamespacedHierarchicalStore<org.junit.platform.engine.support.store.Namespace> createStore(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private NamespacedHierarchicalStore<org.junit.platform.engine.support.store.Namespace> createStore(
private static NamespacedHierarchicalStore<org.junit.platform.engine.support.store.Namespace> createStore(

@@ -29,6 +31,8 @@
@API(status = INTERNAL, since = "5.0")
public class NamespaceAwareStore implements Store {

private static final Logger LOGGER = LoggerFactory.getLogger(NamespaceAwareStore.class);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this logger again.

@@ -100,6 +100,7 @@ private <T> Resource<T> getOrCreateResource(ExtensionContext extensionContext, C
}
}

@SuppressWarnings("deprecation")
class Resource<T> implements CloseableResource {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use AutoCloseable here

@@ -76,6 +76,7 @@ private static boolean notEmpty(Path file) {
}
}

@SuppressWarnings("deprecation")
record OutputDir(Path root) implements ExtensionContext.Store.CloseableResource {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto.

store.put("resource", resource);

((AutoCloseable) extensionContext).close();
assertThat(listener.stream(Level.WARNING)).map(LogRecord::getMessage).noneMatch(msg::equals);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be easier to assert that there are not log messages, WDYT?

*/
// tag::user_guide[]
class HttpServerResource implements CloseableResource {
class HttpServerResource implements AutoCloseable {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also need to update the Asciidoc title for this snippet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Introduce feature flag for auto-closing AutoCloseable in Jupiter's ExtensionContext.Store
2 participants