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 #4252

Closed
wants to merge 57 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
5bfe6ad
Generate ResourceContext for store resources
YongGoose Dec 22, 2024
7992634
Add getStore method in LauncherSession
YongGoose Dec 22, 2024
f3adab1
Add Namespace by reusing Jupiter's Namespace class
YongGoose Jan 6, 2025
e4e60ba
Merge branch 'junit-team:main' into feature/2816
YongGoose Jan 18, 2025
234f7ff
Apply comment
YongGoose Jan 18, 2025
ffde19c
Adding a request-level store to Launcher
YongGoose Jan 19, 2025
4fa576f
Remove logging to SYS_ERR in test
sbrannen Jan 19, 2025
57e20cd
Revise Javadoc and release notes regarding void lookups
sbrannen Jan 19, 2025
30cbe62
Move Kotlin contracts release note to correct section
sbrannen Jan 19, 2025
254ca62
Revise recent changes to @⁠ResourceLock and related APIs
sbrannen Jan 19, 2025
2e14b3d
Revise release notes for 5.12 M1
sbrannen Jan 19, 2025
d583ab0
Fix Javadoc errors
sbrannen Jan 19, 2025
f6eff60
Polish Javadoc in TestTemplateInvocationContextProvider
sbrannen Jan 20, 2025
dde72a9
Polishing
sbrannen Jan 20, 2025
5fd9e88
Remove Void wrapper registration in ReflectionUtils
sbrannen Jan 20, 2025
3f40a7e
Update @⁠since/@⁠API versions in Reflection[Support|Utils] plus polis…
sbrannen Jan 20, 2025
9bddac8
Update Javadoc due to changes in 5fd9e88d07
sbrannen Jan 20, 2025
0fe36c7
Increase test coverage for ReflectionUtils.tryToLoadClass()
sbrannen Jan 20, 2025
2bae68b
Delete cases covered by `Class.forName()`
sormuras Jan 17, 2025
5ca0ced
Fix grammar in Javadoc for NestedMethodSelector
sbrannen Jan 21, 2025
aec4ad9
Update plugin commonCustomUserData to v2.1
renovate[bot] Jan 22, 2025
ba3a4ff
Update actions/stale digest to 5bef64f (#4253)
renovate[bot] Jan 22, 2025
10eeeda
Update github/codeql-action digest to d68b2d4 (#4255)
renovate[bot] Jan 22, 2025
ab41b23
Update codecov/codecov-action digest to 5a605bd (#4257)
renovate[bot] Jan 22, 2025
15539b0
Update graalvm/setup-graalvm digest to aafbedb (#4254)
renovate[bot] Jan 23, 2025
5cb4571
Update actions/attest-build-provenance action to v2.2.0 (#4258)
renovate[bot] Jan 23, 2025
21bb094
Update github/codeql-action digest to dd196fa (#4260)
renovate[bot] Jan 23, 2025
544c3b2
Update plugin plantuml to v8.12
renovate[bot] Jan 23, 2025
3583e01
Use separate local Maven repos when file system type is NTFS (#4259)
marcphilipp Jan 23, 2025
292f6a0
Revert "Use Gradle's new daemon JVM criteria feature"
marcphilipp Jan 23, 2025
9d45d42
Update plugin versions to v0.52.0 (#4262)
renovate[bot] Jan 23, 2025
e78ca1d
Update plugin develocity to v3.19.1
renovate[bot] Jan 23, 2025
73d7f12
Update github/codeql-action digest to ee117c9
renovate[bot] Jan 23, 2025
bf08ea2
Update codecov/codecov-action digest to 0da7aa6
renovate[bot] Jan 24, 2025
6d260da
Remove Twitter/X
marcphilipp Jan 24, 2025
a1a5353
Update codecov/codecov-action digest to 13ce06b
renovate[bot] Jan 26, 2025
9149fb8
Update github/codeql-action digest to f6091c0
renovate[bot] Jan 26, 2025
4c382d0
Update dependency gradle to v8.12.1
renovate[bot] Jan 26, 2025
80a22b6
Update dependency org.apache.groovy:groovy to v4.0.25 (#4273)
renovate[bot] Jan 26, 2025
7b4b972
Revert to older Eclipse version to fix Spotless issue on CI
marcphilipp Jan 26, 2025
ca7bb80
Revert "Revert to older Eclipse version to fix Spotless issue on CI"
marcphilipp Jan 26, 2025
060f4db
Pass enclosing instance types to `DisplayNameGenerators` (#4266)
marcphilipp Jan 26, 2025
511ab7d
Provide runtime enclosing instance types to `ResourceLocksProviders`
marcphilipp Jan 26, 2025
b7349b4
Update dependency com.puppycrawl.tools:checkstyle to v10.21.2 (#4274)
renovate[bot] Jan 27, 2025
7d66196
Re-open completed non-task/-question issues without assigned milestone
marcphilipp Jan 27, 2025
e6446ff
Remove milestone assignment from issues closed as "not planned"
marcphilipp Jan 27, 2025
f93b1fc
Support `@TempDir` constructor injection for Java record classes (#4279)
marcphilipp Jan 27, 2025
54f1996
Apply comment
YongGoose Jan 27, 2025
3fa27e9
Add "Getting Started" section (#4280)
marcphilipp Jan 27, 2025
516a801
Generate ResourceContext for store resources
YongGoose Dec 22, 2024
339c956
Add getStore method in LauncherSession
YongGoose Dec 22, 2024
f92d60f
Add Namespace by reusing Jupiter's Namespace class
YongGoose Jan 6, 2025
96d86c2
Apply comment
YongGoose Jan 18, 2025
82c14be
Adding a request-level store to Launcher
YongGoose Jan 19, 2025
4de6db8
Apply comment
YongGoose Jan 27, 2025
7fdeb72
Merge remote-tracking branch 'origin/feature/2816' into feature/2816
YongGoose Jan 27, 2025
628f8cb
Revert "Apply comment"
YongGoose Jan 27, 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 @@ -418,6 +418,28 @@ 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
*/
Store getSessionLevelStore();

/**
* 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();

/**
* Get the {@link ExecutionMode} associated with the current test or container.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,16 @@ public Store getStore(Namespace namespace) {
return new NamespaceAwareStore(this.valuesStore, namespace);
}

@Override
public Store getSessionLevelStore() {
return getStore(Namespace.create("session"));
}

@Override
public Store getRequestLevelStore() {
return getStore(Namespace.create("request"));
}

@Override
public Set<String> getTags() {
// return modifiable copy
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* 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.platform.engine;

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

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.apiguardian.api.API;
import org.junit.platform.commons.util.Preconditions;

@API(status = STABLE, since = "5.13")
public class Namespace {

/**
* The default, global namespace which allows access to stored data from
* all extensions.
*/
public static final Namespace GLOBAL = Namespace.create(new Object());

/**
* Create a namespace which restricts access to data to all extensions
* which use the same sequence of {@code parts} for creating a namespace.
*
* <p>The order of the {@code parts} is significant.
*
* <p>Internally the {@code parts} are compared using {@link Object#equals(Object)}.
*/
public static Namespace create(Object... parts) {
Preconditions.notEmpty(parts, "parts array must not be null or empty");
Preconditions.containsNoNullElements(parts, "individual parts must not be null");
return new Namespace(new ArrayList<>(Arrays.asList(parts)));
}

private final List<Object> parts;

private Namespace(List<Object> parts) {
this.parts = parts;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Namespace that = (Namespace) o;
return this.parts.equals(that.parts);
}

@Override
public int hashCode() {
return this.parts.hashCode();
}

/**
* Create a new namespace by appending the supplied {@code parts} to the
* existing sequence of parts in this namespace.
*
* @return new namespace; never {@code null}
* @since 5.8
*/
@API(status = STABLE, since = "5.13")
public Namespace append(Object... parts) {
Preconditions.notEmpty(parts, "parts array must not be null or empty");
Preconditions.containsNoNullElements(parts, "individual parts must not be null");
ArrayList<Object> newParts = new ArrayList<>(this.parts.size() + parts.length);
newParts.addAll(this.parts);
Collections.addAll(newParts, parts);
return new Namespace(newParts);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.util.Comparator;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
Expand Down Expand Up @@ -85,6 +86,18 @@ public NamespacedHierarchicalStore<N> newChild() {
return new NamespacedHierarchicalStore<>(this, this.closeAction);
}

/**
* Returns the parent store of this {@code NamespacedHierarchicalStore}.
*
* <p>If this store does not have a parent, an empty {@code Optional} is returned.
*
* @return an {@code Optional} containing the parent store, or an empty {@code Optional} if there is no parent
* @since 5.13
*/
public Optional<NamespacedHierarchicalStore<N>> getParent() {
return Optional.ofNullable(this.parentStore);
}

/**
* Determine if this store has been {@linkplain #close() closed}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import static org.apiguardian.api.API.Status.STABLE;

import org.apiguardian.api.API;
import org.junit.platform.engine.Namespace;
import org.junit.platform.engine.support.store.NamespacedHierarchicalStore;

/**
* The {@code Launcher} API is the main entry point for client code that
Expand Down Expand Up @@ -127,4 +129,5 @@ public interface Launcher {
@API(status = STABLE, since = "1.4")
void execute(TestPlan testPlan, TestExecutionListener... listeners);

NamespacedHierarchicalStore<Namespace> getStore(LauncherDiscoveryRequest launcherDiscoveryRequest);
Copy link
Member

Choose a reason for hiding this comment

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

What's the use case for this method? Test engines don't have access to the Launcher so we'll need to pass this store as part of LauncherDiscoveryRequest and ExecutionRequest, don't we?

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 I need to study a bit more.
I’ll make revisions and add comments by the end of the weekend!

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import static org.apiguardian.api.API.Status.STABLE;

import org.apiguardian.api.API;
import org.junit.platform.engine.Namespace;
import org.junit.platform.engine.support.store.NamespacedHierarchicalStore;
import org.junit.platform.launcher.core.LauncherFactory;

/**
Expand Down Expand Up @@ -47,4 +49,6 @@ public interface LauncherSession extends AutoCloseable {
@Override
void close();

NamespacedHierarchicalStore<Namespace> getStore();

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@
import java.util.Collection;

import org.junit.platform.commons.util.Preconditions;
import org.junit.platform.engine.Namespace;
import org.junit.platform.engine.TestEngine;
import org.junit.platform.engine.support.store.NamespacedHierarchicalStore;
import org.junit.platform.launcher.Launcher;
import org.junit.platform.launcher.LauncherDiscoveryListener;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.junit.platform.launcher.LauncherSession;
import org.junit.platform.launcher.PostDiscoveryFilter;
import org.junit.platform.launcher.TestExecutionListener;
import org.junit.platform.launcher.TestPlan;
Expand All @@ -41,6 +44,8 @@ class DefaultLauncher implements Launcher {
private final EngineExecutionOrchestrator executionOrchestrator = new EngineExecutionOrchestrator(
listenerRegistry.testExecutionListeners);
private final EngineDiscoveryOrchestrator discoveryOrchestrator;
private final LauncherSession launcherSession;
private final NamespacedHierarchicalStore<Namespace> sessionStore;

/**
* Construct a new {@code DefaultLauncher} with the supplied test engines.
Expand All @@ -50,7 +55,8 @@ class DefaultLauncher implements Launcher {
* @param postDiscoveryFilters the additional post discovery filters for
* discovery requests; never {@code null}
*/
DefaultLauncher(Iterable<TestEngine> testEngines, Collection<PostDiscoveryFilter> postDiscoveryFilters) {
DefaultLauncher(Iterable<TestEngine> testEngines, Collection<PostDiscoveryFilter> postDiscoveryFilters,
LauncherConfig config) {
Preconditions.condition(testEngines != null && testEngines.iterator().hasNext(),
() -> "Cannot create Launcher without at least one TestEngine; "
+ "consider adding an engine implementation JAR to the classpath");
Expand All @@ -59,6 +65,8 @@ class DefaultLauncher implements Launcher {
"PostDiscoveryFilter array must not contain null elements");
this.discoveryOrchestrator = new EngineDiscoveryOrchestrator(testEngines,
unmodifiableCollection(postDiscoveryFilters), listenerRegistry.launcherDiscoveryListeners);
this.launcherSession = LauncherFactory.openSession(config);
this.sessionStore = launcherSession.getStore();
}

@Override
Expand Down Expand Up @@ -94,6 +102,11 @@ public void execute(TestPlan testPlan, TestExecutionListener... listeners) {
execute((InternalTestPlan) testPlan, listeners);
}

@Override
public NamespacedHierarchicalStore<Namespace> getStore(LauncherDiscoveryRequest launcherDiscoveryRequest) {
return new NamespacedHierarchicalStore<>(this.sessionStore);
}

private LauncherDiscoveryResult discover(LauncherDiscoveryRequest discoveryRequest,
EngineDiscoveryOrchestrator.Phase phase) {
return discoveryOrchestrator.discover(discoveryRequest, phase);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import java.util.function.Supplier;

import org.junit.platform.commons.PreconditionViolationException;
import org.junit.platform.engine.Namespace;
import org.junit.platform.engine.support.store.NamespacedHierarchicalStore;
import org.junit.platform.launcher.Launcher;
import org.junit.platform.launcher.LauncherDiscoveryListener;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
Expand Down Expand Up @@ -43,6 +45,7 @@ public void close() {
private final LauncherInterceptor interceptor;
private final LauncherSessionListener listener;
private final DelegatingLauncher launcher;
private final NamespacedHierarchicalStore<Namespace> store;

DefaultLauncherSession(List<LauncherInterceptor> interceptors, Supplier<LauncherSessionListener> listenerSupplier,
Supplier<Launcher> launcherSupplier) {
Expand All @@ -58,6 +61,7 @@ public void close() {
}
this.launcher = new DelegatingLauncher(launcher);
listener.launcherSessionOpened(this);
this.store = new NamespacedHierarchicalStore<>(null);
}

@Override
Expand All @@ -78,6 +82,11 @@ public void close() {
}
}

@Override
public NamespacedHierarchicalStore<Namespace> getStore() {
return store;
}

private static class ClosedLauncher implements Launcher {

static final ClosedLauncher INSTANCE = new ClosedLauncher();
Expand Down Expand Up @@ -109,6 +118,11 @@ public void execute(LauncherDiscoveryRequest launcherDiscoveryRequest, TestExecu
public void execute(TestPlan testPlan, TestExecutionListener... listeners) {
throw new PreconditionViolationException("Launcher session has already been closed");
}

@Override
public NamespacedHierarchicalStore<Namespace> getStore(LauncherDiscoveryRequest launcherDiscoveryRequest) {
throw new PreconditionViolationException("Launcher session has already been closed");
}
}

private static LauncherInterceptor composite(List<LauncherInterceptor> interceptors) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

package org.junit.platform.launcher.core;

import org.junit.platform.engine.Namespace;
import org.junit.platform.engine.support.store.NamespacedHierarchicalStore;
import org.junit.platform.launcher.Launcher;
import org.junit.platform.launcher.LauncherDiscoveryListener;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
Expand Down Expand Up @@ -52,4 +54,9 @@ public void execute(TestPlan testPlan, TestExecutionListener... listeners) {
delegate.execute(testPlan, listeners);
}

@Override
public NamespacedHierarchicalStore<Namespace> getStore(LauncherDiscoveryRequest launcherDiscoveryRequest) {
return delegate.getStore(launcherDiscoveryRequest);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ private static DefaultLauncher createDefaultLauncher(LauncherConfig config,
Set<TestEngine> engines = collectTestEngines(config);
List<PostDiscoveryFilter> filters = collectPostDiscoveryFilters(config);

DefaultLauncher launcher = new DefaultLauncher(engines, filters);
DefaultLauncher launcher = new DefaultLauncher(engines, filters, config);

registerLauncherDiscoveryListeners(config, launcher);
registerTestExecutionListeners(config, launcher, configurationParameters);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import java.util.List;
import java.util.function.Supplier;

import org.junit.platform.engine.Namespace;
import org.junit.platform.engine.support.store.NamespacedHierarchicalStore;
import org.junit.platform.launcher.Launcher;
import org.junit.platform.launcher.LauncherDiscoveryListener;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
Expand Down Expand Up @@ -71,6 +73,13 @@ public void execute(TestPlan testPlan, TestExecutionListener... listeners) {
}
}

@Override
public NamespacedHierarchicalStore<Namespace> getStore(LauncherDiscoveryRequest launcherDiscoveryRequest) {
try (LauncherSession session = createSession()) {
return session.getLauncher().getStore(launcherDiscoveryRequest);
}
}

private LauncherSession createSession() {
LauncherSession session = new DefaultLauncherSession(interceptorFactory.get(), sessionListenerSupplier,
launcherSupplier);
Expand Down
Loading