Skip to content

Commit

Permalink
Provide runtime enclosing instance types to ResourceLocksProviders
Browse files Browse the repository at this point in the history
Resolves #4163.
  • Loading branch information
marcphilipp committed Jan 25, 2025
1 parent 6d260da commit 82d8cc9
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 27 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
* {@linkplain org.junit.jupiter.api.Nested @Nested} test class.
*
* <p>Invoked in case:
* <ul>
Expand All @@ -75,16 +76,18 @@ default Set<Lock> provideForClass(Class<?> testClass) {
* the same semantics as annotating a nested test class with an analogous
* {@code @ResourceLock(value, mode)} declaration.
*
* @param enclosingInstanceTypes the runtime types of the enclosing
* instances; 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 +100,15 @@ default Set<Lock> provideForNestedClass(Class<?> testClass) {
* has the same semantics as annotating a test method
* with analogous {@code @ResourceLock(value, mode)}.
*
* @param enclosingInstanceTypes the runtime types of the enclosing
* instances; 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 @@ -16,6 +16,7 @@
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;
Expand Down Expand Up @@ -97,7 +98,11 @@ public ExclusiveResourceCollector getExclusiveResourceCollector() {

@Override
public Set<ResourceLocksProvider.Lock> evaluateResourceLocksProvider(ResourceLocksProvider provider) {
return provider.provideForMethod(getTestClass(), getTestMethod());
List<Class<?>> enclosingInstanceTypes = getParent() //
.filter(ClassBasedTestDescriptor.class::isInstance) //
.map(parent -> ((ClassBasedTestDescriptor) parent).getEnclosingTestClasses()) //
.orElseGet(Collections::emptyList);
return provider.provideForMethod(enclosingInstanceTypes, getTestClass(), getTestMethod());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ protected TestInstances instantiateTestClass(JupiterEngineExecutionContext paren

@Override
public Set<ResourceLocksProvider.Lock> evaluateResourceLocksProvider(ResourceLocksProvider provider) {
return provider.provideForNestedClass(getTestClass());
return provider.provideForNestedClass(getEnclosingTestClasses(), getTestClass());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import static org.mockito.Mockito.when;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;

Expand Down Expand Up @@ -360,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 @@ -416,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 @@ -451,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
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import static org.junit.platform.testkit.engine.EventConditions.test;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;

Expand Down Expand Up @@ -60,8 +61,14 @@ void methodLevelProviderInNestedClass() {
assertThat(events.filter(event(test(), finishedSuccessfully())::matches)).hasSize(2);
}

@Test
void providesAccessToRuntimeEnclosingInstances() {
var events = execute(SubClassLevelProviderTestCase.class);
assertThat(events.filter(event(test(), finishedSuccessfully())::matches)).hasSize(2);
}

private Stream<Event> execute(Class<?> testCase) {
return executeTestsForClass(testCase).allEvents().stream();
return executeTestsForClass(testCase).allEvents().debug().stream();
}

// -------------------------------------------------------------------------
Expand Down Expand Up @@ -108,28 +115,35 @@ static class Provider implements ResourceLocksProvider {
private static boolean isProvideForNestedClassCalled = false;
private static boolean isProvideForNestedTestMethodCalled = false;

private Class<?> testClass;

@Override
public Set<Lock> provideForClass(Class<?> testClass) {
this.testClass = testClass;
isProvideForClassCalled = true;
assertEquals(ClassLevelProviderTestCase.class, testClass);
assertThat(testClass).isAssignableTo(ClassLevelProviderTestCase.class);
return emptySet();
}

@Override
public Set<Lock> provideForNestedClass(Class<?> testClass) {
public Set<Lock> provideForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> testClass) {
isProvideForNestedClassCalled = true;
assertEquals(List.of(this.testClass), enclosingInstanceTypes);
assertEquals(ClassLevelProviderTestCase.NestedClass.class, testClass);
return emptySet();
}

@Override
public Set<Lock> provideForMethod(Class<?> testClass, Method testMethod) {
if (testClass == ClassLevelProviderTestCase.class) {
public Set<Lock> provideForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,
Method testMethod) {
if (ClassLevelProviderTestCase.class.isAssignableFrom(testClass)) {
assertEquals(List.of(), enclosingInstanceTypes);
assertEquals("test", testMethod.getName());
isProvideForTestMethodCalled = true;
return emptySet();
}
if (testClass == ClassLevelProviderTestCase.NestedClass.class) {
assertEquals(List.of(this.testClass), enclosingInstanceTypes);
assertEquals("nestedTest", testMethod.getName());
isProvideForNestedTestMethodCalled = true;
return emptySet();
Expand All @@ -140,6 +154,9 @@ public Set<Lock> provideForMethod(Class<?> testClass, Method testMethod) {
}
}

static class SubClassLevelProviderTestCase extends ClassLevelProviderTestCase {
}

@SuppressWarnings("JUnitMalformedDeclaration")
static class NestedClassLevelProviderTestCase {

Expand Down Expand Up @@ -177,15 +194,18 @@ public Set<Lock> provideForClass(Class<?> testClass) {
}

@Override
public Set<Lock> provideForNestedClass(Class<?> testClass) {
public Set<Lock> provideForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> testClass) {
isProvideForNestedClassCalled = true;
assertEquals(List.of(NestedClassLevelProviderTestCase.class), enclosingInstanceTypes);
assertEquals(NestedClass.class, testClass);
return emptySet();
}

@Override
public Set<Lock> provideForMethod(Class<?> testClass, Method testMethod) {
public Set<Lock> provideForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,
Method testMethod) {
isProvideForMethodCalled = true;
assertEquals(List.of(NestedClassLevelProviderTestCase.class), enclosingInstanceTypes);
assertEquals(NestedClassLevelProviderTestCase.NestedClass.class, testClass);
assertEquals("nestedTest", testMethod.getName());
return emptySet();
Expand Down Expand Up @@ -226,14 +246,16 @@ public Set<Lock> provideForClass(Class<?> testClass) {
}

@Override
public Set<Lock> provideForNestedClass(Class<?> testClass) {
public Set<Lock> provideForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> testClass) {
fail("'provideForNestedClass' should not be called");
return emptySet();
}

@Override
public Set<Lock> provideForMethod(Class<?> testClass, Method testMethod) {
public Set<Lock> provideForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,
Method testMethod) {
isProvideForMethodCalled = true;
assertEquals(List.of(), enclosingInstanceTypes);
assertEquals(MethodLevelProviderTestCase.class, testClass);
assertEquals("test", testMethod.getName());
return emptySet();
Expand Down Expand Up @@ -274,14 +296,16 @@ public Set<Lock> provideForClass(Class<?> testClass) {
}

@Override
public Set<Lock> provideForNestedClass(Class<?> testClass) {
public Set<Lock> provideForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> testClass) {
fail("'provideForNestedClass' should not be called");
return emptySet();
}

@Override
public Set<Lock> provideForMethod(Class<?> testClass, Method testMethod) {
public Set<Lock> provideForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,
Method testMethod) {
isProvideForMethodCalled = true;
assertEquals(List.of(MethodLevelProviderInNestedClassTestCase.class), enclosingInstanceTypes);
assertEquals(MethodLevelProviderInNestedClassTestCase.NestedClass.class, testClass);
assertEquals("nestedTest", testMethod.getName());
return emptySet();
Expand Down

0 comments on commit 82d8cc9

Please sign in to comment.