diff --git a/documentation/src/test/java/example/sharedresources/DynamicSharedResourcesDemo.java b/documentation/src/test/java/example/sharedresources/DynamicSharedResourcesDemo.java index 31f4c37e6bb3..4abe7297e357 100644 --- a/documentation/src/test/java/example/sharedresources/DynamicSharedResourcesDemo.java +++ b/documentation/src/test/java/example/sharedresources/DynamicSharedResourcesDemo.java @@ -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; @@ -68,7 +69,8 @@ void canSetCustomPropertyToBanana() { static class Provider implements ResourceLocksProvider { @Override - public Set provideForMethod(Class testClass, Method testMethod) { + public Set provideForMethod(List> enclosingInstanceTypes, Class testClass, + Method testMethod) { ResourceAccessMode mode = testMethod.getName().startsWith("canSet") ? READ_WRITE : READ; return Collections.singleton(new Lock(SYSTEM_PROPERTIES, mode)); } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ResourceLocksProvider.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ResourceLocksProvider.java index d94024d99f3b..a629249f9fde 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ResourceLocksProvider.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ResourceLocksProvider.java @@ -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; @@ -43,7 +44,7 @@ public interface ResourceLocksProvider { /** - * Add shared resources to a test class. + * Add shared resources for a test class. * *

Invoked in case a test class or its parent class is annotated with * {@code @ResourceLock(providers)}. @@ -60,8 +61,8 @@ default Set 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. * *

Invoked in case: *

    @@ -75,16 +76,18 @@ default Set 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 provideForNestedClass(Class testClass) { + default Set provideForNestedClass(List> enclosingInstanceTypes, Class testClass) { return emptySet(); } /** - * Add shared resources to a test method. + * Add shared resources for a test method. * *

    Invoked in case: *

      @@ -97,13 +100,15 @@ default Set 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 provideForMethod(Class testClass, Method testMethod) { + default Set provideForMethod(List> enclosingInstanceTypes, Class testClass, Method testMethod) { return emptySet(); } diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/MethodBasedTestDescriptor.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/MethodBasedTestDescriptor.java index 525b58293709..ecfd88eadc81 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/MethodBasedTestDescriptor.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/MethodBasedTestDescriptor.java @@ -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; @@ -97,7 +98,11 @@ public ExclusiveResourceCollector getExclusiveResourceCollector() { @Override public Set evaluateResourceLocksProvider(ResourceLocksProvider provider) { - return provider.provideForMethod(getTestClass(), getTestMethod()); + List> enclosingInstanceTypes = getParent() // + .filter(ClassBasedTestDescriptor.class::isInstance) // + .map(parent -> ((ClassBasedTestDescriptor) parent).getEnclosingTestClasses()) // + .orElseGet(Collections::emptyList); + return provider.provideForMethod(enclosingInstanceTypes, getTestClass(), getTestMethod()); } @Override diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/NestedClassTestDescriptor.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/NestedClassTestDescriptor.java index 6d4dc1e2d088..48c4b574d772 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/NestedClassTestDescriptor.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/NestedClassTestDescriptor.java @@ -88,7 +88,7 @@ protected TestInstances instantiateTestClass(JupiterEngineExecutionContext paren @Override public Set evaluateResourceLocksProvider(ResourceLocksProvider provider) { - return provider.provideForNestedClass(getTestClass()); + return provider.provideForNestedClass(getEnclosingTestClasses(), getTestClass()); } } diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/api/parallel/ResourceLockAnnotationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/parallel/ResourceLockAnnotationTests.java index 2025c6e0ad49..45b8cdeabd7f 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/api/parallel/ResourceLockAnnotationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/parallel/ResourceLockAnnotationTests.java @@ -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; @@ -360,7 +361,8 @@ public Set provideForClass(Class testClass) { } @Override - public Set provideForMethod(Class testClass, Method testMethod) { + public Set provideForMethod(List> enclosingInstanceTypes, Class testClass, + Method testMethod) { return Set.of(new Lock("b1")); } } @@ -368,7 +370,8 @@ public Set provideForMethod(Class testClass, Method testMethod) { static class MethodLevelProvider implements ResourceLocksProvider { @Override - public Set provideForMethod(Class testClass, Method testMethod) { + public Set provideForMethod(List> enclosingInstanceTypes, Class testClass, + Method testMethod) { return Set.of(new Lock("b2")); } } @@ -376,7 +379,7 @@ public Set provideForMethod(Class testClass, Method testMethod) { static class NestedClassLevelProvider implements ResourceLocksProvider { @Override - public Set provideForNestedClass(Class testClass) { + public Set provideForNestedClass(List> enclosingInstanceTypes, Class testClass) { return Set.of(new Lock("c1"), new Lock("c2", ResourceAccessMode.READ)); } } @@ -416,12 +419,13 @@ public Set provideForClass(Class testClass) { static class SecondClassLevelProvider implements ResourceLocksProvider { @Override - public Set provideForMethod(Class testClass, Method testMethod) { + public Set provideForMethod(List> enclosingInstanceTypes, Class testClass, + Method testMethod) { return Set.of(new Lock("b2", ResourceAccessMode.READ)); } @Override - public Set provideForNestedClass(Class testClass) { + public Set provideForNestedClass(List> enclosingInstanceTypes, Class testClass) { return Set.of(new Lock("c2")); } } @@ -429,7 +433,7 @@ public Set provideForNestedClass(Class testClass) { static class NestedClassLevelProvider implements ResourceLocksProvider { @Override - public Set provideForNestedClass(Class testClass) { + public Set provideForNestedClass(List> enclosingInstanceTypes, Class testClass) { return Set.of(new Lock("c3")); } } @@ -451,7 +455,8 @@ void test() { static class Provider implements ResourceLocksProvider { @Override - public Set provideForMethod(Class testClass, Method testMethod) { + public Set provideForMethod(List> enclosingInstanceTypes, Class testClass, + Method testMethod) { return Set.of(new Lock("a1")); } } diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/api/parallel/ResourceLocksProviderTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/parallel/ResourceLocksProviderTests.java index 8a73be42b8ee..609f15d1832b 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/api/parallel/ResourceLocksProviderTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/parallel/ResourceLocksProviderTests.java @@ -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; @@ -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 execute(Class testCase) { - return executeTestsForClass(testCase).allEvents().stream(); + return executeTestsForClass(testCase).allEvents().debug().stream(); } // ------------------------------------------------------------------------- @@ -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 provideForClass(Class testClass) { + this.testClass = testClass; isProvideForClassCalled = true; - assertEquals(ClassLevelProviderTestCase.class, testClass); + assertThat(testClass).isAssignableTo(ClassLevelProviderTestCase.class); return emptySet(); } @Override - public Set provideForNestedClass(Class testClass) { + public Set provideForNestedClass(List> enclosingInstanceTypes, Class testClass) { isProvideForNestedClassCalled = true; + assertEquals(List.of(this.testClass), enclosingInstanceTypes); assertEquals(ClassLevelProviderTestCase.NestedClass.class, testClass); return emptySet(); } @Override - public Set provideForMethod(Class testClass, Method testMethod) { - if (testClass == ClassLevelProviderTestCase.class) { + public Set provideForMethod(List> 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(); @@ -140,6 +154,9 @@ public Set provideForMethod(Class testClass, Method testMethod) { } } + static class SubClassLevelProviderTestCase extends ClassLevelProviderTestCase { + } + @SuppressWarnings("JUnitMalformedDeclaration") static class NestedClassLevelProviderTestCase { @@ -177,15 +194,18 @@ public Set provideForClass(Class testClass) { } @Override - public Set provideForNestedClass(Class testClass) { + public Set provideForNestedClass(List> enclosingInstanceTypes, Class testClass) { isProvideForNestedClassCalled = true; + assertEquals(List.of(NestedClassLevelProviderTestCase.class), enclosingInstanceTypes); assertEquals(NestedClass.class, testClass); return emptySet(); } @Override - public Set provideForMethod(Class testClass, Method testMethod) { + public Set provideForMethod(List> enclosingInstanceTypes, Class testClass, + Method testMethod) { isProvideForMethodCalled = true; + assertEquals(List.of(NestedClassLevelProviderTestCase.class), enclosingInstanceTypes); assertEquals(NestedClassLevelProviderTestCase.NestedClass.class, testClass); assertEquals("nestedTest", testMethod.getName()); return emptySet(); @@ -226,14 +246,16 @@ public Set provideForClass(Class testClass) { } @Override - public Set provideForNestedClass(Class testClass) { + public Set provideForNestedClass(List> enclosingInstanceTypes, Class testClass) { fail("'provideForNestedClass' should not be called"); return emptySet(); } @Override - public Set provideForMethod(Class testClass, Method testMethod) { + public Set provideForMethod(List> enclosingInstanceTypes, Class testClass, + Method testMethod) { isProvideForMethodCalled = true; + assertEquals(List.of(), enclosingInstanceTypes); assertEquals(MethodLevelProviderTestCase.class, testClass); assertEquals("test", testMethod.getName()); return emptySet(); @@ -274,14 +296,16 @@ public Set provideForClass(Class testClass) { } @Override - public Set provideForNestedClass(Class testClass) { + public Set provideForNestedClass(List> enclosingInstanceTypes, Class testClass) { fail("'provideForNestedClass' should not be called"); return emptySet(); } @Override - public Set provideForMethod(Class testClass, Method testMethod) { + public Set provideForMethod(List> enclosingInstanceTypes, Class testClass, + Method testMethod) { isProvideForMethodCalled = true; + assertEquals(List.of(MethodLevelProviderInNestedClassTestCase.class), enclosingInstanceTypes); assertEquals(MethodLevelProviderInNestedClassTestCase.NestedClass.class, testClass); assertEquals("nestedTest", testMethod.getName()); return emptySet();