Skip to content

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

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

Open
wants to merge 29 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in d498200 !

@@ -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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That seems like a better way. 🙂

*/
// 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.

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, Since HttpServerResource is used as an example to explain CloseableResource, I think it would be better to revert it back to CloseableResource instead of AutoCloseable.

.`ExtensionContext.Store.CloseableResource`
NOTE: An extension context store is bound to its extension context lifecycle. When an
extension context lifecycle ends it closes its associated store. All stored values
that are instances of `CloseableResource` are notified by an invocation of their `close()`
method in the inverse order they were added in.

An example implementation of `CloseableResource` is shown below, using an `HttpServer`
resource.

[source,java,indent=0]
.`HttpServer` resource implementing `CloseableResource`

Using an example with AutoCloseable while explaining Closeable might confuse the reader.
@marcphilipp WDYT? 🤔

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 it should be used to explain AutoCloseable instead and we should revamp that section to mention that older releases used CloseableResource instead which is still supported but no longer recommended etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it should be used to explain AutoCloseable instead and we should revamp that section to mention that older releases used CloseableResource instead which is still supported but no longer recommended etc.

Done in 3a96c24 !

YongGoose and others added 3 commits April 15, 2025 18:57
YongGoose and others added 7 commits April 15, 2025 19:08
…scriptor/AbstractExtensionContext.java

Co-authored-by: Marc Philipp <[email protected]>
…scriptor/AbstractExtensionContext.java

Co-authored-by: Marc Philipp <[email protected]>
…scriptor/AbstractExtensionContext.java

Co-authored-by: Marc Philipp <[email protected]>
…nfig/JupiterConfiguration.java

Co-authored-by: Marc Philipp <[email protected]>
…nfig/JupiterConfiguration.java

Co-authored-by: Marc Philipp <[email protected]>
@YongGoose YongGoose requested a review from marcphilipp April 15, 2025 10:46
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