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

Provide mechanism for managing resources across engines and executions #4281

Draft
wants to merge 38 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
9bbe717
Generate ResourceContext for store resources
YongGoose Dec 22, 2024
f4d7f6b
Add getStore method in LauncherSession
YongGoose Dec 22, 2024
17bcccb
Add Namespace by reusing Jupiter's Namespace class
YongGoose Jan 6, 2025
4b766ad
Apply comment
YongGoose Jan 18, 2025
6ad80de
Adding a request-level store to Launcher
YongGoose Jan 19, 2025
d5528fb
Apply comment
YongGoose Jan 27, 2025
7bae8bc
Generate ResourceContext for store resources
YongGoose Dec 22, 2024
28ac373
Add Namespace by reusing Jupiter's Namespace class
YongGoose Jan 6, 2025
4130c36
Apply comment
YongGoose Jan 18, 2025
e6a8543
Adding a request-level store to Launcher
YongGoose Jan 19, 2025
bdf400d
Apply comment
YongGoose Jan 27, 2025
5d3a14b
Revert "Apply comment"
YongGoose Jan 27, 2025
02ef45b
Apply comment
YongGoose Jan 27, 2025
f7e5996
Update junit-platform-engine/src/main/java/org/junit/platform/engine/…
YongGoose Feb 12, 2025
ae5a110
Update junit-platform-launcher/src/main/java/org/junit/platform/launc…
YongGoose Feb 12, 2025
33f5fb8
Add API annotation
YongGoose Feb 12, 2025
9fd6fd8
Move Namespace class
YongGoose Feb 12, 2025
42a0089
Apply comment
YongGoose Feb 12, 2025
5d2424f
Merge branch 'main' into feature/2816
YongGoose Feb 12, 2025
7d98e9a
Apply Namespace parameter
YongGoose Feb 13, 2025
fd9023e
Merge branch 'main' into feature/2816
YongGoose Feb 14, 2025
60f66f5
Outline management of session-/request-level stores
marcphilipp Feb 16, 2025
4624808
Use session-/request-level store in Jupiter engine
marcphilipp Feb 16, 2025
bac5250
Use request-level store as parent of engine-level store
marcphilipp Feb 16, 2025
9c24186
fixup! Outline management of session-/request-level stores
marcphilipp Feb 16, 2025
972ad0a
Fix test
marcphilipp Feb 16, 2025
6b4ee03
Fix ExecutionRequest's `NamespacedHierarchicalStore`
YongGoose Feb 18, 2025
d72090c
Merge branch 'main' into feature/2816
YongGoose Feb 21, 2025
3f56d7d
Merge branch 'main' into feature/2816
YongGoose Feb 22, 2025
b2c95c4
Fix post-merge compile error
marcphilipp Feb 24, 2025
e0a43ef
Limit session-/request-level store availability to execution
marcphilipp Feb 24, 2025
92abb5d
Remove broken Javadoc link
marcphilipp Feb 24, 2025
18148d5
Merge branch 'main' into feature/2816
YongGoose Feb 25, 2025
62bcb8f
Revert changes
YongGoose Feb 25, 2025
9fd3ba2
Add JupiterEngineTests
YongGoose Feb 27, 2025
efcc719
Merge branch 'main' into feature/2816
YongGoose Mar 2, 2025
b3877c7
Merge branch 'main' into feature/2816
YongGoose Mar 3, 2025
1702fbf
Add NamespaceTests
YongGoose Mar 3, 2025
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 @@ -11,6 +11,7 @@
package org.junit.jupiter.api.extension;

import static org.apiguardian.api.API.Status.EXPERIMENTAL;
import static org.apiguardian.api.API.Status.INTERNAL;
import static org.apiguardian.api.API.Status.STABLE;

import java.lang.reflect.AnnotatedElement;
Expand Down Expand Up @@ -418,6 +419,29 @@ default void publishReportEntry(String value) {
*/
Store getStore(Namespace namespace);

/**
* Returns the store for session-level data.
*
* <p>This store is used to store data that is scoped to the session level.
* The data stored in this store will be available throughout the entire session.
*
* @return the store for session-level data
* @since 5.13
*/
@API(status = EXPERIMENTAL, since = "5.13")
Store getSessionLevelStore(Namespace namespace);

/**
* Returns the store for request-level data.
*
* <p>This store is used to store data that is scoped to the request level.
* The data stored in this store will be available only for the duration of the current request.
*
* @return the store for request-level data
* @since 5.13
*/
Store getRequestLevelStore(Namespace namespace);

/**
* Get the {@link ExecutionMode} associated with the current test or container.
*
Expand Down Expand Up @@ -744,6 +768,11 @@ public Namespace append(Object... parts) {
Collections.addAll(newParts, parts);
return new Namespace(newParts);
}

@API(status = INTERNAL, since = "5.13")
public Object[] getParts() {
return parts.toArray();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.junit.jupiter.engine.config.DefaultJupiterConfiguration;
import org.junit.jupiter.engine.config.JupiterConfiguration;
import org.junit.jupiter.engine.descriptor.JupiterEngineDescriptor;
import org.junit.jupiter.engine.descriptor.LauncherStoreFacade;
import org.junit.jupiter.engine.discovery.DiscoverySelectorResolver;
import org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;
import org.junit.jupiter.engine.support.JupiterThrowableCollectorFactory;
Expand Down Expand Up @@ -82,8 +83,8 @@ protected HierarchicalTestExecutorService createExecutorService(ExecutionRequest

@Override
protected JupiterEngineExecutionContext createExecutionContext(ExecutionRequest request) {
return new JupiterEngineExecutionContext(request.getEngineExecutionListener(),
getJupiterConfiguration(request));
return new JupiterEngineExecutionContext(request.getEngineExecutionListener(), getJupiterConfiguration(request),
new LauncherStoreFacade(request.getRequestLevelStore()));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import org.junit.jupiter.api.parallel.ExecutionMode;
import org.junit.jupiter.engine.config.JupiterConfiguration;
import org.junit.jupiter.engine.execution.DefaultExecutableInvoker;
import org.junit.jupiter.engine.execution.NamespaceAwareStore;
import org.junit.jupiter.engine.extension.ExtensionContextInternal;
import org.junit.jupiter.engine.extension.ExtensionRegistry;
import org.junit.platform.commons.JUnitException;
Expand All @@ -52,7 +51,8 @@
*/
abstract class AbstractExtensionContext<T extends TestDescriptor> implements ExtensionContextInternal, AutoCloseable {

private static final NamespacedHierarchicalStore.CloseAction<Namespace> CLOSE_RESOURCES = (__, ___, value) -> {
private static final NamespacedHierarchicalStore.CloseAction<org.junit.platform.engine.support.store.Namespace> CLOSE_RESOURCES = (
__, ___, value) -> {
if (value instanceof CloseableResource) {
((CloseableResource) value).close();
}
Expand All @@ -63,12 +63,14 @@ abstract class AbstractExtensionContext<T extends TestDescriptor> implements Ext
private final T testDescriptor;
private final Set<String> tags;
private final JupiterConfiguration configuration;
private final NamespacedHierarchicalStore<Namespace> valuesStore;
private final NamespacedHierarchicalStore<org.junit.platform.engine.support.store.Namespace> valuesStore;
private final ExecutableInvoker executableInvoker;
private final ExtensionRegistry extensionRegistry;
private final LauncherStoreFacade launcherStoreFacade;

AbstractExtensionContext(ExtensionContext parent, EngineExecutionListener engineExecutionListener, T testDescriptor,
JupiterConfiguration configuration, ExtensionRegistry extensionRegistry) {
JupiterConfiguration configuration, ExtensionRegistry extensionRegistry,
LauncherStoreFacade launcherStoreFacade) {

Preconditions.notNull(testDescriptor, "TestDescriptor must not be null");
Preconditions.notNull(configuration, "JupiterConfiguration must not be null");
Expand All @@ -78,8 +80,9 @@ abstract class AbstractExtensionContext<T extends TestDescriptor> implements Ext
this.engineExecutionListener = engineExecutionListener;
this.testDescriptor = testDescriptor;
this.configuration = configuration;
this.valuesStore = createStore(parent);
this.valuesStore = createStore(parent, launcherStoreFacade);
this.extensionRegistry = extensionRegistry;
this.launcherStoreFacade = launcherStoreFacade;

// @formatter:off
this.tags = testDescriptor.getTags().stream()
Expand All @@ -88,9 +91,13 @@ abstract class AbstractExtensionContext<T extends TestDescriptor> implements Ext
// @formatter:on
}

private static NamespacedHierarchicalStore<Namespace> createStore(ExtensionContext parent) {
NamespacedHierarchicalStore<Namespace> parentStore = null;
if (parent != null) {
private static NamespacedHierarchicalStore<org.junit.platform.engine.support.store.Namespace> createStore(
ExtensionContext parent, LauncherStoreFacade launcherStoreFacade) {
NamespacedHierarchicalStore<org.junit.platform.engine.support.store.Namespace> parentStore;
if (parent == null) {
parentStore = launcherStoreFacade.getRequestLevelStore();
}
else {
parentStore = ((AbstractExtensionContext<?>) parent).valuesStore;
}
return new NamespacedHierarchicalStore<>(parentStore, CLOSE_RESOURCES);
Expand Down Expand Up @@ -188,8 +195,17 @@ protected T getTestDescriptor() {

@Override
public Store getStore(Namespace namespace) {
Preconditions.notNull(namespace, "Namespace must not be null");
return new NamespaceAwareStore(this.valuesStore, namespace);
return launcherStoreFacade.getStoreAdapter(this.valuesStore, namespace);
}

@Override
public Store getSessionLevelStore(Namespace namespace) {
return launcherStoreFacade.getSessionLevelStore(namespace);
}

@Override
public Store getRequestLevelStore(Namespace namespace) {
return launcherStoreFacade.getRequestLevelStore(namespace);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ public final JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext
ThrowableCollector throwableCollector = createThrowableCollector();
ClassExtensionContext extensionContext = new ClassExtensionContext(context.getExtensionContext(),
context.getExecutionListener(), this, this.lifecycle, context.getConfiguration(), registry,
throwableCollector);
context.getLauncherStoreFacade(), throwableCollector);

// @formatter:off
return context.extend()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@ final class ClassExtensionContext extends AbstractExtensionContext<ClassBasedTes

ClassExtensionContext(ExtensionContext parent, EngineExecutionListener engineExecutionListener,
ClassBasedTestDescriptor testDescriptor, Lifecycle lifecycle, JupiterConfiguration configuration,
ExtensionRegistry extensionRegistry, ThrowableCollector throwableCollector) {
ExtensionRegistry extensionRegistry, LauncherStoreFacade launcherStoreFacade,
ThrowableCollector throwableCollector) {

super(parent, engineExecutionListener, testDescriptor, configuration, extensionRegistry);
super(parent, engineExecutionListener, testDescriptor, configuration, extensionRegistry, launcherStoreFacade);

this.lifecycle = lifecycle;
this.throwableCollector = throwableCollector;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ final class ContainerTemplateInvocationExtensionContext

ContainerTemplateInvocationExtensionContext(ExtensionContext parent,
EngineExecutionListener engineExecutionListener, ContainerTemplateInvocationTestDescriptor testDescriptor,
JupiterConfiguration configuration, ExtensionRegistry extensionRegistry) {
super(parent, engineExecutionListener, testDescriptor, configuration, extensionRegistry);
JupiterConfiguration configuration, ExtensionRegistry extensionRegistry,
LauncherStoreFacade launcherStoreFacade) {
super(parent, engineExecutionListener, testDescriptor, configuration, extensionRegistry, launcherStoreFacade);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ public JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext conte
registry = childRegistry;
}
ExtensionContext extensionContext = new ContainerTemplateInvocationExtensionContext(
context.getExtensionContext(), context.getExecutionListener(), this, context.getConfiguration(), registry);
context.getExtensionContext(), context.getExecutionListener(), this, context.getConfiguration(), registry,
context.getLauncherStoreFacade());
this.invocationContext.prepareInvocation(extensionContext);
return context.extend() //
.withExtensionContext(extensionContext) //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ class DynamicExtensionContext extends AbstractExtensionContext<DynamicNodeTestDe

DynamicExtensionContext(ExtensionContext parent, EngineExecutionListener engineExecutionListener,
DynamicNodeTestDescriptor testDescriptor, JupiterConfiguration configuration,
ExtensionRegistry extensionRegistry) {
super(parent, engineExecutionListener, testDescriptor, configuration, extensionRegistry);
ExtensionRegistry extensionRegistry, LauncherStoreFacade launcherStoreFacade) {
super(parent, engineExecutionListener, testDescriptor, configuration, extensionRegistry, launcherStoreFacade);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ public String getLegacyReportingName() {
@Override
public JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext context) {
ExtensionContext extensionContext = new DynamicExtensionContext(context.getExtensionContext(),
context.getExecutionListener(), this, context.getConfiguration(), context.getExtensionRegistry());
context.getExecutionListener(), this, context.getConfiguration(), context.getExtensionRegistry(),
context.getLauncherStoreFacade());

// @formatter:off
return context.extend()
.withExtensionContext(extensionContext)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext conte
context.getConfiguration());
EngineExecutionListener executionListener = context.getExecutionListener();
ExtensionContext extensionContext = new JupiterEngineExtensionContext(executionListener, this,
context.getConfiguration(), extensionRegistry);
context.getConfiguration(), extensionRegistry, context.getLauncherStoreFacade());

// @formatter:off
return context.extend()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ final class JupiterEngineExtensionContext extends AbstractExtensionContext<Jupit

JupiterEngineExtensionContext(EngineExecutionListener engineExecutionListener,
JupiterEngineDescriptor testDescriptor, JupiterConfiguration configuration,
ExtensionRegistry extensionRegistry) {
ExtensionRegistry extensionRegistry, LauncherStoreFacade launcherStoreFacade) {

super(null, engineExecutionListener, testDescriptor, configuration, extensionRegistry);
super(null, engineExecutionListener, testDescriptor, configuration, extensionRegistry, launcherStoreFacade);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2015-2025 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v2.0 which
* accompanies this distribution and is available at
*
* https://www.eclipse.org/legal/epl-v20.html
*/

package org.junit.jupiter.engine.descriptor;

import static org.apiguardian.api.API.Status.INTERNAL;

import org.apiguardian.api.API;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.engine.execution.NamespaceAwareStore;
import org.junit.platform.commons.JUnitException;
import org.junit.platform.commons.util.Preconditions;
import org.junit.platform.engine.support.store.Namespace;
import org.junit.platform.engine.support.store.NamespacedHierarchicalStore;

@API(status = INTERNAL, since = "5.13")
public class LauncherStoreFacade {

private final NamespacedHierarchicalStore<Namespace> requestLevelStore;
private final NamespacedHierarchicalStore<Namespace> sessionLevelStore;

public LauncherStoreFacade(NamespacedHierarchicalStore<Namespace> requestLevelStore) {
this.requestLevelStore = requestLevelStore;
this.sessionLevelStore = requestLevelStore.getParent().orElseThrow(
() -> new JUnitException("Request-level store must have a parent"));
}

NamespacedHierarchicalStore<Namespace> getRequestLevelStore() {
return this.requestLevelStore;
}

ExtensionContext.Store getRequestLevelStore(ExtensionContext.Namespace namespace) {
return getStoreAdapter(this.requestLevelStore, namespace);
}

ExtensionContext.Store getSessionLevelStore(ExtensionContext.Namespace namespace) {
return getStoreAdapter(this.sessionLevelStore, namespace);
}

NamespaceAwareStore<Namespace> getStoreAdapter(NamespacedHierarchicalStore<Namespace> valuesStore,
ExtensionContext.Namespace namespace) {
Preconditions.notNull(namespace, "Namespace must not be null");
return new NamespaceAwareStore<>(valuesStore, convert(namespace));
}

private Namespace convert(ExtensionContext.Namespace namespace) {
return Namespace.create(namespace.getParts());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ final class MethodExtensionContext extends AbstractExtensionContext<TestMethodTe

MethodExtensionContext(ExtensionContext parent, EngineExecutionListener engineExecutionListener,
TestMethodTestDescriptor testDescriptor, JupiterConfiguration configuration,
ExtensionRegistry extensionRegistry, ThrowableCollector throwableCollector) {
ExtensionRegistry extensionRegistry, LauncherStoreFacade launcherStoreFacade,
ThrowableCollector throwableCollector) {

super(parent, engineExecutionListener, testDescriptor, configuration, extensionRegistry);
super(parent, engineExecutionListener, testDescriptor, configuration, extensionRegistry, launcherStoreFacade);

this.throwableCollector = throwableCollector;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,10 @@ public JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext conte
MutableExtensionRegistry registry = populateNewExtensionRegistry(context);
ThrowableCollector throwableCollector = createThrowableCollector();
MethodExtensionContext extensionContext = new MethodExtensionContext(context.getExtensionContext(),
context.getExecutionListener(), this, context.getConfiguration(), registry, throwableCollector);
context.getExecutionListener(), this, context.getConfiguration(), registry,
context.getLauncherStoreFacade(), throwableCollector);
throwableCollector.execute(() -> prepareExtensionContext(extensionContext));

// @formatter:off
JupiterEngineExecutionContext newContext = context.extend()
.withExtensionRegistry(registry)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ final class TestTemplateExtensionContext extends AbstractExtensionContext<TestTe

TestTemplateExtensionContext(ExtensionContext parent, EngineExecutionListener engineExecutionListener,
TestTemplateTestDescriptor testDescriptor, JupiterConfiguration configuration,
ExtensionRegistry extensionRegistry, TestInstances testInstances) {
ExtensionRegistry extensionRegistry, LauncherStoreFacade launcherStoreFacade, TestInstances testInstances) {

super(parent, engineExecutionListener, testDescriptor, configuration, extensionRegistry);
super(parent, engineExecutionListener, testDescriptor, configuration, extensionRegistry, launcherStoreFacade);
this.testInstances = testInstances;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ public JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext conte
TestInstances testInstances = context.getExtensionContext().getTestInstances().orElse(null);

ExtensionContext extensionContext = new TestTemplateExtensionContext(context.getExtensionContext(),
context.getExecutionListener(), this, context.getConfiguration(), registry, testInstances);
context.getExecutionListener(), this, context.getConfiguration(), registry,
context.getLauncherStoreFacade(), testInstances);

// @formatter:off
return context.extend()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.apiguardian.api.API;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.engine.config.JupiterConfiguration;
import org.junit.jupiter.engine.descriptor.LauncherStoreFacade;
import org.junit.jupiter.engine.extension.MutableExtensionRegistry;
import org.junit.platform.commons.JUnitException;
import org.junit.platform.engine.EngineExecutionListener;
Expand All @@ -33,9 +34,9 @@ public class JupiterEngineExecutionContext implements EngineExecutionContext {
private boolean beforeAllCallbacksExecuted = false;
private boolean beforeAllMethodsExecuted = false;

public JupiterEngineExecutionContext(EngineExecutionListener executionListener,
JupiterConfiguration configuration) {
this(new State(executionListener, configuration));
public JupiterEngineExecutionContext(EngineExecutionListener executionListener, JupiterConfiguration configuration,
LauncherStoreFacade launcherStoreFacade) {
this(new State(executionListener, configuration, launcherStoreFacade));
}

private JupiterEngineExecutionContext(State state) {
Expand All @@ -62,6 +63,10 @@ public JupiterConfiguration getConfiguration() {
return this.state.configuration;
}

public LauncherStoreFacade getLauncherStoreFacade() {
return this.state.launcherStoreFacade;
}

public TestInstancesProvider getTestInstancesProvider() {
return this.state.testInstancesProvider;
}
Expand Down Expand Up @@ -119,14 +124,17 @@ private static final class State implements Cloneable {

final EngineExecutionListener executionListener;
final JupiterConfiguration configuration;
final LauncherStoreFacade launcherStoreFacade;
TestInstancesProvider testInstancesProvider;
MutableExtensionRegistry extensionRegistry;
ExtensionContext extensionContext;
ThrowableCollector throwableCollector;

State(EngineExecutionListener executionListener, JupiterConfiguration configuration) {
State(EngineExecutionListener executionListener, JupiterConfiguration configuration,
LauncherStoreFacade launcherStoreFacade) {
this.executionListener = executionListener;
this.configuration = configuration;
this.launcherStoreFacade = launcherStoreFacade;
}

@Override
Expand Down
Loading