diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-6.0.2.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-6.0.2.adoc index a0ba07732b62..382420cc04bc 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-6.0.2.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-6.0.2.adoc @@ -35,7 +35,8 @@ repository on GitHub. [[release-notes-6.0.2-junit-jupiter-bug-fixes]] ==== Bug Fixes -* ❓ +* Allow using `@ResourceLock` on classes annotated with `@ClassTemplate` (or + `@ParameterizedClass`). [[release-notes-6.0.2-junit-jupiter-deprecations-and-breaking-changes]] ==== Deprecations and Breaking Changes diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassTemplateInvocationTestDescriptor.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassTemplateInvocationTestDescriptor.java index 9d3037b6059b..afa3e639daac 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassTemplateInvocationTestDescriptor.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassTemplateInvocationTestDescriptor.java @@ -10,6 +10,7 @@ package org.junit.jupiter.engine.descriptor; +import static java.util.Collections.emptySet; import static java.util.Objects.requireNonNull; import static org.apiguardian.api.API.Status.INTERNAL; import static org.junit.jupiter.engine.descriptor.CallbackSupport.invokeAfterCallbacks; @@ -36,6 +37,7 @@ import org.junit.jupiter.engine.extension.MutableExtensionRegistry; import org.junit.platform.engine.TestSource; import org.junit.platform.engine.UniqueId; +import org.junit.platform.engine.support.hierarchical.ExclusiveResource; import org.junit.platform.engine.support.hierarchical.ThrowableCollector; /** @@ -112,6 +114,12 @@ public Function> getResou // --- Node ---------------------------------------------------------------- + @Override + public Set getExclusiveResources() { + // Resources are already collected and returned by the enclosing ClassTemplateTestDescriptor + return emptySet(); + } + @Override public JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext context) { MutableExtensionRegistry registry = context.getExtensionRegistry(); diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/engine/ClassTemplateInvocationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/ClassTemplateInvocationTests.java index 8be2bab63816..c0f04016515c 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/engine/ClassTemplateInvocationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/ClassTemplateInvocationTests.java @@ -79,6 +79,7 @@ import org.junit.jupiter.api.extension.ParameterResolutionException; import org.junit.jupiter.api.extension.ParameterResolver; import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.parallel.ResourceLock; import org.junit.jupiter.engine.descriptor.ClassTemplateInvocationTestDescriptor; import org.junit.jupiter.engine.descriptor.ClassTemplateTestDescriptor; import org.junit.jupiter.engine.descriptor.ClassTestDescriptor; @@ -95,6 +96,7 @@ import org.junit.platform.engine.UniqueId; import org.junit.platform.engine.discovery.DiscoverySelectors; import org.junit.platform.engine.reporting.ReportEntry; +import org.junit.platform.engine.support.hierarchical.ExclusiveResource; import org.junit.platform.testkit.engine.EngineExecutionResults; import org.opentest4j.AssertionFailedError; import org.opentest4j.TestAbortedException; @@ -1004,6 +1006,24 @@ void ignoresComposedAnnotations() { assertThat(engineDescriptor.getDescendants()).isEmpty(); } + @Test + void classTemplateWithResourceLockCollectsExclusiveResources() { + var results = discoverTestsForClass(ClassTemplateWithResourceLockTestCase.class); + var classTemplateDescriptor = (ClassTemplateTestDescriptor) getOnlyElement( + results.getEngineDescriptor().getChildren()); + + assertThat(classTemplateDescriptor.getExclusiveResources()).extracting( + ExclusiveResource::getKey).containsExactly("test-resource"); + } + + @Test + void classTemplateWithResourceLockExecutesSuccessfully() { + var results = executeTestsForClass(ClassTemplateWithResourceLockTestCase.class); + + results.testEvents().assertStatistics(stats -> stats.started(2).succeeded(2)); + results.containerEvents().assertStatistics(stats -> stats.started(4).succeeded(4)); + } + // ------------------------------------------------------------------- private static Stream allReportEntryValues(EngineExecutionResults results) { @@ -1567,4 +1587,15 @@ void test() { } } + @SuppressWarnings("JUnitMalformedDeclaration") + @ClassTemplate + @ExtendWith(TwoInvocationsClassTemplateInvocationContextProvider.class) + @ResourceLock("test-resource") + static class ClassTemplateWithResourceLockTestCase { + @Test + void test() { + // This test verifies that @ResourceLock works with @ClassTemplate (issue #5155) + } + } + }