Skip to content

Commit

Permalink
Provide runtime enclosing instance types to ResourceLocksProviders
Browse files Browse the repository at this point in the history
Make API consistent with `DisplayNameGenerator` prior to releasing it in
5.12.

Resolves #4163.

---------

Co-authored-by: Sam Brannen <[email protected]>
  • Loading branch information
marcphilipp and sbrannen authored Jan 26, 2025
1 parent 060f4db commit 511ab7d
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.Set;

Expand Down Expand Up @@ -68,7 +69,8 @@ void canSetCustomPropertyToBanana() {
static class Provider implements ResourceLocksProvider {

@Override
public Set<Lock> provideForMethod(Class<?> testClass, Method testMethod) {
public Set<Lock> provideForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,
Method testMethod) {
ResourceAccessMode mode = testMethod.getName().startsWith("canSet") ? READ_WRITE : READ;
return Collections.singleton(new Lock(SYSTEM_PROPERTIES, mode));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import static org.apiguardian.api.API.Status.EXPERIMENTAL;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Objects;
import java.util.Set;

Expand Down Expand Up @@ -43,7 +44,7 @@
public interface ResourceLocksProvider {

/**
* Add shared resources to a test class.
* Add shared resources for a test class.
*
* <p>Invoked in case a test class or its parent class is annotated with
* {@code @ResourceLock(providers)}.
Expand All @@ -60,8 +61,8 @@ default Set<Lock> provideForClass(Class<?> testClass) {
}

/**
* Add shared resources to a {@linkplain org.junit.jupiter.api.Nested @Nested}
* test class.
* Add shared resources for a
* {@link org.junit.jupiter.api.Nested @Nested} test class.
*
* <p>Invoked in case:
* <ul>
Expand All @@ -75,16 +76,24 @@ default Set<Lock> provideForClass(Class<?> testClass) {
* the same semantics as annotating a nested test class with an analogous
* {@code @ResourceLock(value, mode)} declaration.
*
* @implNote The classes supplied as {@code enclosingInstanceTypes} may
* differ from the classes returned from invocations of
* {@link Class#getEnclosingClass()} &mdash; for example, when a nested test
* class is inherited from a superclass.
*
* @param enclosingInstanceTypes the runtime types of the enclosing
* instances for the test class, ordered from outermost to innermost,
* excluding {@code testClass}; never {@code null}
* @param testClass a nested test class for which to add shared resources
* @return a set of {@link Lock}; may be empty
* @see org.junit.jupiter.api.Nested @Nested
*/
default Set<Lock> provideForNestedClass(Class<?> testClass) {
default Set<Lock> provideForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> testClass) {
return emptySet();
}

/**
* Add shared resources to a test method.
* Add shared resources for a test method.
*
* <p>Invoked in case:
* <ul>
Expand All @@ -97,13 +106,24 @@ default Set<Lock> provideForNestedClass(Class<?> testClass) {
* has the same semantics as annotating a test method
* with analogous {@code @ResourceLock(value, mode)}.
*
* @implNote The classes supplied as {@code enclosingInstanceTypes} may
* differ from the classes returned from invocations of
* {@link Class#getEnclosingClass()} &mdash; for example, when a nested test
* class is inherited from a superclass. Similarly, the class instance
* supplied as {@code testClass} may differ from the class returned by
* {@code testMethod.getDeclaringClass()} &mdash; for example, when a test
* method is inherited from a superclass.
*
* @param enclosingInstanceTypes the runtime types of the enclosing
* instances for the test class, ordered from outermost to innermost,
* excluding {@code testClass}; never {@code null}
* @param testClass the test class or {@link org.junit.jupiter.api.Nested @Nested}
* test class that contains the {@code testMethod}
* @param testMethod a test method for which to add shared resources
* @return a set of {@link Lock}; may be empty
* @see org.junit.jupiter.api.Nested @Nested
*/
default Set<Lock> provideForMethod(Class<?> testClass, Method testMethod) {
default Set<Lock> provideForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass, Method testMethod) {
return emptySet();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;

import org.apiguardian.api.API;
import org.junit.jupiter.api.extension.TestInstances;
Expand Down Expand Up @@ -79,8 +80,8 @@ protected TestInstances instantiateTestClass(JupiterEngineExecutionContext paren
}

@Override
public Set<ResourceLocksProvider.Lock> evaluateResourceLocksProvider(ResourceLocksProvider provider) {
return provider.provideForClass(getTestClass());
public Function<ResourceLocksProvider, Set<ResourceLocksProvider.Lock>> getResourceLocksProviderEvaluator() {
return provider -> provider.provideForClass(getTestClass());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,17 @@
import static org.apiguardian.api.API.Status.INTERNAL;
import static org.junit.jupiter.api.parallel.ResourceLockTarget.CHILDREN;
import static org.junit.jupiter.engine.descriptor.DisplayNameUtils.determineDisplayNameForMethod;
import static org.junit.jupiter.engine.descriptor.ResourceLockAware.enclosingInstanceTypesDependentResourceLocksProviderEvaluator;
import static org.junit.platform.commons.util.CollectionUtils.forEachInReverseOrder;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

import org.apiguardian.api.API;
Expand Down Expand Up @@ -97,8 +100,18 @@ public ExclusiveResourceCollector getExclusiveResourceCollector() {
}

@Override
public Set<ResourceLocksProvider.Lock> evaluateResourceLocksProvider(ResourceLocksProvider provider) {
return provider.provideForMethod(getTestClass(), getTestMethod());
public Function<ResourceLocksProvider, Set<ResourceLocksProvider.Lock>> getResourceLocksProviderEvaluator() {
return enclosingInstanceTypesDependentResourceLocksProviderEvaluator(this::getEnclosingTestClasses,
(provider, enclosingInstanceTypes) -> provider.provideForMethod(enclosingInstanceTypes, getTestClass(),
getTestMethod()));
}

private List<Class<?>> getEnclosingTestClasses() {
return getParent() //
.filter(ClassBasedTestDescriptor.class::isInstance) //
.map(ClassBasedTestDescriptor.class::cast) //
.map(ClassBasedTestDescriptor::getEnclosingTestClasses) //
.orElseGet(Collections::emptyList);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@
import static java.util.Collections.emptyList;
import static org.apiguardian.api.API.Status.INTERNAL;
import static org.junit.jupiter.engine.descriptor.DisplayNameUtils.createDisplayNameSupplierForNestedClass;
import static org.junit.jupiter.engine.descriptor.ResourceLockAware.enclosingInstanceTypesDependentResourceLocksProviderEvaluator;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;

import org.apiguardian.api.API;
Expand Down Expand Up @@ -94,8 +96,9 @@ protected TestInstances instantiateTestClass(JupiterEngineExecutionContext paren
}

@Override
public Set<ResourceLocksProvider.Lock> evaluateResourceLocksProvider(ResourceLocksProvider provider) {
return provider.provideForNestedClass(getTestClass());
public Function<ResourceLocksProvider, Set<ResourceLocksProvider.Lock>> getResourceLocksProviderEvaluator() {
return enclosingInstanceTypesDependentResourceLocksProviderEvaluator(this::getEnclosingTestClasses, (provider,
enclosingInstanceTypes) -> provider.provideForNestedClass(enclosingInstanceTypes, getTestClass()));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;

import org.junit.jupiter.api.parallel.ResourceLocksProvider;
Expand All @@ -35,27 +39,48 @@ default Stream<ExclusiveResource> determineExclusiveResources() {
parent = parent.getParent().orElse(null);
}

Function<ResourceLocksProvider, Set<ResourceLocksProvider.Lock>> evaluator = getResourceLocksProviderEvaluator();

if (ancestors.isEmpty()) {
return determineOwnExclusiveResources();
return determineOwnExclusiveResources(evaluator);
}

Stream<ExclusiveResource> parentStaticResourcesForChildren = ancestors.getLast() //
.getExclusiveResourceCollector().getStaticResourcesFor(CHILDREN);

Stream<ExclusiveResource> ancestorDynamicResources = ancestors.stream() //
.map(ResourceLockAware::getExclusiveResourceCollector) //
.flatMap(collector -> collector.getDynamicResources(this::evaluateResourceLocksProvider));
.flatMap(collector -> collector.getDynamicResources(evaluator));

return Stream.of(ancestorDynamicResources, parentStaticResourcesForChildren, determineOwnExclusiveResources())//
return Stream.of(ancestorDynamicResources, parentStaticResourcesForChildren,
determineOwnExclusiveResources(evaluator))//
.flatMap(s -> s);
}

default Stream<ExclusiveResource> determineOwnExclusiveResources() {
return this.getExclusiveResourceCollector().getAllExclusiveResources(this::evaluateResourceLocksProvider);
default Stream<ExclusiveResource> determineOwnExclusiveResources(
Function<ResourceLocksProvider, Set<ResourceLocksProvider.Lock>> providerToLocks) {
return this.getExclusiveResourceCollector().getAllExclusiveResources(providerToLocks);
}

ExclusiveResourceCollector getExclusiveResourceCollector();

Set<ResourceLocksProvider.Lock> evaluateResourceLocksProvider(ResourceLocksProvider provider);
Function<ResourceLocksProvider, Set<ResourceLocksProvider.Lock>> getResourceLocksProviderEvaluator();

static Function<ResourceLocksProvider, Set<ResourceLocksProvider.Lock>> enclosingInstanceTypesDependentResourceLocksProviderEvaluator(
Supplier<List<Class<?>>> enclosingInstanceTypesSupplier,
BiFunction<ResourceLocksProvider, List<Class<?>>, Set<ResourceLocksProvider.Lock>> evaluator) {
return new Function<ResourceLocksProvider, Set<ResourceLocksProvider.Lock>>() {

private List<Class<?>> enclosingInstanceTypes;

@Override
public Set<ResourceLocksProvider.Lock> apply(ResourceLocksProvider provider) {
if (this.enclosingInstanceTypes == null) {
this.enclosingInstanceTypes = enclosingInstanceTypesSupplier.get();
}
return evaluator.apply(provider, this.enclosingInstanceTypes);
}
};
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -361,23 +361,25 @@ public Set<Lock> provideForClass(Class<?> testClass) {
}

@Override
public Set<Lock> provideForMethod(Class<?> testClass, Method testMethod) {
public Set<Lock> provideForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,
Method testMethod) {
return Set.of(new Lock("b1"));
}
}

static class MethodLevelProvider implements ResourceLocksProvider {

@Override
public Set<Lock> provideForMethod(Class<?> testClass, Method testMethod) {
public Set<Lock> provideForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,
Method testMethod) {
return Set.of(new Lock("b2"));
}
}

static class NestedClassLevelProvider implements ResourceLocksProvider {

@Override
public Set<Lock> provideForNestedClass(Class<?> testClass) {
public Set<Lock> provideForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> testClass) {
return Set.of(new Lock("c1"), new Lock("c2", ResourceAccessMode.READ));
}
}
Expand Down Expand Up @@ -417,20 +419,21 @@ public Set<Lock> provideForClass(Class<?> testClass) {
static class SecondClassLevelProvider implements ResourceLocksProvider {

@Override
public Set<Lock> provideForMethod(Class<?> testClass, Method testMethod) {
public Set<Lock> provideForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,
Method testMethod) {
return Set.of(new Lock("b2", ResourceAccessMode.READ));
}

@Override
public Set<Lock> provideForNestedClass(Class<?> testClass) {
public Set<Lock> provideForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> testClass) {
return Set.of(new Lock("c2"));
}
}

static class NestedClassLevelProvider implements ResourceLocksProvider {

@Override
public Set<Lock> provideForNestedClass(Class<?> testClass) {
public Set<Lock> provideForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> testClass) {
return Set.of(new Lock("c3"));
}
}
Expand All @@ -452,7 +455,8 @@ void test() {
static class Provider implements ResourceLocksProvider {

@Override
public Set<Lock> provideForMethod(Class<?> testClass, Method testMethod) {
public Set<Lock> provideForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,
Method testMethod) {
return Set.of(new Lock("a1"));
}
}
Expand Down
Loading

0 comments on commit 511ab7d

Please sign in to comment.