Skip to content

Commit c5359ec

Browse files
committed
Close all arguments, despite early failures
1 parent 96ec3cd commit c5359ec

File tree

12 files changed

+283
-51
lines changed

12 files changed

+283
-51
lines changed

Diff for: documentation/src/docs/asciidoc/release-notes/release-notes-5.13.0-M1.adoc

+6
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ repository on GitHub.
4040
fewer parameters.
4141
* `AutoCloseable` arguments returned by an `ArgumentsProvider` are now closed even if they
4242
are wrapped with `Named`.
43+
* `AutoCloseable` arguments returned by an `ArgumentsProvider` are now closed even if a
44+
failure happens prior to invoking the parameterized method.
4345

4446
[[release-notes-5.13.0-M1-junit-jupiter-deprecations-and-breaking-changes]]
4547
==== Deprecations and Breaking Changes
@@ -54,6 +56,10 @@ repository on GitHub.
5456
times. This may be used, for example, to inject different parameters to be used by all
5557
tests in the container template class or to set up each invocation of the container
5658
template differently.
59+
* New `TestTemplateInvocationContext.prepareInvocation(ExtensionContext)` callback method
60+
allows preparing the `ExtensionContext` before the test template method is invoked. This
61+
may be used, for example, to store entries in its `Store` to benefit from its cleanup
62+
support or for retrieval by other extensions.
5763

5864

5965
[[release-notes-5.13.0-M1-junit-vintage]]

Diff for: junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ContainerTemplateInvocationContext.java

+12
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,16 @@ default List<Extension> getAdditionalExtensions() {
6767
return emptyList();
6868
}
6969

70+
/**
71+
* Prepare the imminent invocation of the container template.
72+
*
73+
* <p>This may be used, for example, to store entries in the
74+
* {@link ExtensionContext.Store Store} to benefit from its cleanup support
75+
* or for retrieval by other extensions.
76+
*
77+
* @param context The invocation-level extension context.
78+
*/
79+
default void prepareInvocation(ExtensionContext context) {
80+
}
81+
7082
}

Diff for: junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestTemplateInvocationContext.java

+15
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
package org.junit.jupiter.api.extension;
1212

1313
import static java.util.Collections.emptyList;
14+
import static org.apiguardian.api.API.Status.EXPERIMENTAL;
1415
import static org.apiguardian.api.API.Status.STABLE;
1516

1617
import java.util.List;
@@ -66,4 +67,18 @@ default List<Extension> getAdditionalExtensions() {
6667
return emptyList();
6768
}
6869

70+
/**
71+
* Prepare the imminent invocation of the test template.
72+
*
73+
* <p>This may be used, for example, to store entries in the
74+
* {@link ExtensionContext.Store Store} to benefit from its cleanup support
75+
* or for retrieval by other extensions.
76+
*
77+
* @param context The invocation-level extension context.
78+
* @since 5.13
79+
*/
80+
@API(status = EXPERIMENTAL, since = "5.13")
81+
default void prepareInvocation(ExtensionContext context) {
82+
}
83+
6984
}

Diff for: junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ContainerTemplateInvocationTestDescriptor.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ public JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext conte
119119
}
120120
ExtensionContext extensionContext = new ContainerTemplateInvocationExtensionContext(
121121
context.getExtensionContext(), context.getExecutionListener(), this, context.getConfiguration(), registry);
122+
this.invocationContext.prepareInvocation(extensionContext);
122123
return context.extend() //
123124
.withExtensionContext(extensionContext) //
124125
.withExtensionRegistry(registry) //
@@ -134,8 +135,9 @@ public JupiterEngineExecutionContext execute(JupiterEngineExecutionContext conte
134135
}
135136

136137
@Override
137-
public void cleanUp(JupiterEngineExecutionContext context) {
138+
public void cleanUp(JupiterEngineExecutionContext context) throws Exception {
138139
// forget invocationContext so it can be garbage collected
139140
this.invocationContext = null;
141+
super.cleanUp(context);
140142
}
141143
}

Diff for: junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ContainerTemplateTestDescriptor.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -178,10 +178,11 @@ public Set<ExclusiveResource> getExclusiveResources() {
178178
}
179179

180180
@Override
181-
public void cleanUp(JupiterEngineExecutionContext context) {
181+
public void cleanUp(JupiterEngineExecutionContext context) throws Exception {
182182
this.childrenPrototypes.clear();
183183
this.childrenPrototypesByIndex.clear();
184184
this.dynamicDescendantFilter.allowAll();
185+
super.cleanUp(context);
185186
}
186187

187188
@Override

Diff for: junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestMethodTestDescriptor.java

+5
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ public JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext conte
117117
ThrowableCollector throwableCollector = createThrowableCollector();
118118
MethodExtensionContext extensionContext = new MethodExtensionContext(context.getExtensionContext(),
119119
context.getExecutionListener(), this, context.getConfiguration(), registry, throwableCollector);
120+
throwableCollector.execute(() -> prepareExtensionContext(extensionContext));
120121
// @formatter:off
121122
JupiterEngineExecutionContext newContext = context.extend()
122123
.withExtensionRegistry(registry)
@@ -131,6 +132,10 @@ public JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext conte
131132
return newContext;
132133
}
133134

135+
protected void prepareExtensionContext(ExtensionContext extensionContext) {
136+
// nothing to do by default
137+
}
138+
134139
protected MutableExtensionRegistry populateNewExtensionRegistry(JupiterEngineExecutionContext context) {
135140
MutableExtensionRegistry registry = populateNewExtensionRegistryFromExtendWithAnnotation(
136141
context.getExtensionRegistry(), getTestMethod());

Diff for: junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestTemplateInvocationTestDescriptor.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.function.UnaryOperator;
1919

2020
import org.apiguardian.api.API;
21+
import org.junit.jupiter.api.extension.ExtensionContext;
2122
import org.junit.jupiter.api.extension.InvocationInterceptor;
2223
import org.junit.jupiter.api.extension.TestTemplateInvocationContext;
2324
import org.junit.jupiter.engine.config.JupiterConfiguration;
@@ -76,15 +77,20 @@ public String getLegacyReportingName() {
7677
@Override
7778
protected MutableExtensionRegistry populateNewExtensionRegistry(JupiterEngineExecutionContext context) {
7879
MutableExtensionRegistry registry = super.populateNewExtensionRegistry(context);
79-
invocationContext.getAdditionalExtensions().forEach(
80+
this.invocationContext.getAdditionalExtensions().forEach(
8081
extension -> registry.registerExtension(extension, invocationContext));
8182
return registry;
8283
}
8384

85+
@Override
86+
protected void prepareExtensionContext(ExtensionContext extensionContext) {
87+
this.invocationContext.prepareInvocation(extensionContext);
88+
}
89+
8490
@Override
8591
public void after(JupiterEngineExecutionContext context) {
8692
// forget invocationContext so it can be garbage collected
87-
invocationContext = null;
93+
this.invocationContext = null;
8894
}
8995

9096
}

Diff for: junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestInvocationContext.java

+35
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@
1212

1313
import java.util.Arrays;
1414
import java.util.List;
15+
import java.util.concurrent.atomic.AtomicInteger;
1516

1617
import org.junit.jupiter.api.extension.Extension;
18+
import org.junit.jupiter.api.extension.ExtensionContext;
19+
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
20+
import org.junit.jupiter.api.extension.ExtensionContext.Store;
1721
import org.junit.jupiter.api.extension.TestTemplateInvocationContext;
1822
import org.junit.jupiter.params.provider.Arguments;
1923

@@ -22,6 +26,8 @@
2226
*/
2327
class ParameterizedTestInvocationContext implements TestTemplateInvocationContext {
2428

29+
private static final Namespace NAMESPACE = Namespace.create(ParameterizedTestInvocationContext.class);
30+
2531
private final ParameterizedTestNameFormatter formatter;
2632
private final ParameterizedTestMethodContext methodContext;
2733
private final EvaluatedArgumentSet arguments;
@@ -48,10 +54,39 @@ public List<Extension> getAdditionalExtensions() {
4854
new ArgumentCountValidator(this.methodContext, this.arguments));
4955
}
5056

57+
@Override
58+
public void prepareInvocation(ExtensionContext context) {
59+
if (this.methodContext.annotation.autoCloseArguments()) {
60+
Store store = context.getStore(NAMESPACE);
61+
AtomicInteger argumentIndex = new AtomicInteger();
62+
63+
Arrays.stream(this.arguments.getAllPayloads()) //
64+
.filter(AutoCloseable.class::isInstance) //
65+
.map(AutoCloseable.class::cast) //
66+
.map(CloseableArgument::new) //
67+
.forEach(closeable -> store.put(argumentIndex.incrementAndGet(), closeable));
68+
}
69+
}
70+
5171
private int determineConsumedArgumentCount(int totalLength) {
5272
return methodContext.hasAggregator() //
5373
? totalLength //
5474
: Math.min(totalLength, methodContext.getParameterCount());
5575
}
5676

77+
private static class CloseableArgument implements Store.CloseableResource {
78+
79+
private final AutoCloseable autoCloseable;
80+
81+
CloseableArgument(AutoCloseable autoCloseable) {
82+
this.autoCloseable = autoCloseable;
83+
}
84+
85+
@Override
86+
public void close() throws Throwable {
87+
this.autoCloseable.close();
88+
}
89+
90+
}
91+
5792
}

Diff for: junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestParameterResolver.java

+1-45
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,16 @@
1212

1313
import java.lang.reflect.Executable;
1414
import java.lang.reflect.Method;
15-
import java.util.Arrays;
16-
import java.util.concurrent.atomic.AtomicInteger;
1715

18-
import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
1916
import org.junit.jupiter.api.extension.ExtensionContext;
20-
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
21-
import org.junit.jupiter.api.extension.ExtensionContext.Store;
2217
import org.junit.jupiter.api.extension.ParameterContext;
2318
import org.junit.jupiter.api.extension.ParameterResolutionException;
2419
import org.junit.jupiter.api.extension.ParameterResolver;
25-
import org.junit.platform.commons.support.AnnotationSupport;
2620

2721
/**
2822
* @since 5.0
2923
*/
30-
class ParameterizedTestParameterResolver implements ParameterResolver, AfterTestExecutionCallback {
31-
32-
private static final Namespace NAMESPACE = Namespace.create(ParameterizedTestParameterResolver.class);
24+
class ParameterizedTestParameterResolver implements ParameterResolver {
3325

3426
private final ParameterizedTestMethodContext methodContext;
3527
private final EvaluatedArgumentSet arguments;
@@ -81,40 +73,4 @@ public Object resolveParameter(ParameterContext parameterContext, ExtensionConte
8173
this.invocationIndex);
8274
}
8375

84-
/**
85-
* @since 5.8
86-
*/
87-
@Override
88-
public void afterTestExecution(ExtensionContext context) {
89-
ParameterizedTest parameterizedTest = AnnotationSupport.findAnnotation(context.getRequiredTestMethod(),
90-
ParameterizedTest.class).get();
91-
if (!parameterizedTest.autoCloseArguments()) {
92-
return;
93-
}
94-
95-
Store store = context.getStore(NAMESPACE);
96-
AtomicInteger argumentIndex = new AtomicInteger();
97-
98-
Arrays.stream(this.arguments.getAllPayloads()) //
99-
.filter(AutoCloseable.class::isInstance) //
100-
.map(AutoCloseable.class::cast) //
101-
.map(CloseableArgument::new) //
102-
.forEach(closeable -> store.put("closeableArgument#" + argumentIndex.incrementAndGet(), closeable));
103-
}
104-
105-
private static class CloseableArgument implements Store.CloseableResource {
106-
107-
private final AutoCloseable autoCloseable;
108-
109-
CloseableArgument(AutoCloseable autoCloseable) {
110-
this.autoCloseable = autoCloseable;
111-
}
112-
113-
@Override
114-
public void close() throws Throwable {
115-
this.autoCloseable.close();
116-
}
117-
118-
}
119-
12076
}

0 commit comments

Comments
 (0)