diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ExclusiveResources.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ExclusiveResources.java index f9f341685ef3..4f01cf7d211a 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ExclusiveResources.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ExclusiveResources.java @@ -20,9 +20,10 @@ import org.apiguardian.api.API; +// Which 'status' and 'since' values should I use? @API(status = STABLE, since = "?") @Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.TYPE, ElementType.METHOD }) +@Target(ElementType.TYPE) @Inherited public @interface ExclusiveResources { diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ExclusiveResourcesProvider.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ExclusiveResourcesProvider.java index c0f143a05691..6a3fd2c4e71f 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ExclusiveResourcesProvider.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ExclusiveResourcesProvider.java @@ -18,13 +18,14 @@ import org.apiguardian.api.API; +// Which 'status' and 'since' values should I use? @API(status = STABLE, since = "?") public interface ExclusiveResourcesProvider { default Set provideForClass(Class testClass) { return Collections.emptySet(); } - default Set provideForMethod(Class testClass, Method testMethod) { + default Set provideForMethod(Method testMethod) { return Collections.emptySet(); } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/MethodNameBasedExclusiveResourcesProvider.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/MethodNameBasedExclusiveResourcesProvider.java new file mode 100644 index 000000000000..bdfce5fe6bb0 --- /dev/null +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/MethodNameBasedExclusiveResourcesProvider.java @@ -0,0 +1,37 @@ +/* + * Copyright 2015-2024 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.jupiter.api.parallel; + +import static org.apiguardian.api.API.Status.STABLE; + +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.Set; + +import org.apiguardian.api.API; + +// Which 'status' and 'since' values should I use? +@API(status = STABLE, since = "?") +public final class MethodNameBasedExclusiveResourcesProvider implements ExclusiveResourcesProvider { + + private static final ExclusiveResource LOCK = ExclusiveResource.readWriteLockOf("method_lock"); + + public MethodNameBasedExclusiveResourcesProvider() { + } + + @Override + public Set provideForMethod(Method testMethod) { + if (testMethod.getName().contains("give_me_lock")) { + return Collections.singleton(LOCK); + } + return Collections.emptySet(); + } +} diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/MyExclusiveResourcesProvider.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/MyExclusiveResourcesProvider.java deleted file mode 100644 index 6f32fbb77c65..000000000000 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/MyExclusiveResourcesProvider.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2015-2024 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.jupiter.api.parallel; - -import static java.util.stream.Collectors.toSet; -import static org.apiguardian.api.API.Status.STABLE; - -import java.lang.reflect.Method; -import java.util.Set; -import java.util.stream.Stream; - -import org.apiguardian.api.API; - -@API(status = STABLE, since = "?") -public final class MyExclusiveResourcesProvider implements ExclusiveResourcesProvider { - - public MyExclusiveResourcesProvider() { - } - - @Override - public Set provideForClass(Class testClass) { - // Distribute locks depending on class name, package name, declared fields etc. - // ... - return Stream.of(ExclusiveResource.readWriteLockOf("class_lock")).collect(toSet()); - } - - @Override - public Set provideForMethod(Class testClass, Method testMethod) { - // Distribute locks depending on method name, parameters etc. - // ... - return Stream.of(ExclusiveResource.readWriteLockOf("method_lock")).collect(toSet()); - } -} diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/JupiterTestDescriptor.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/JupiterTestDescriptor.java index 72ae33386876..48968421e24b 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/JupiterTestDescriptor.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/JupiterTestDescriptor.java @@ -196,10 +196,10 @@ Set getExclusiveResourcesFromAnnotation(AnnotatedElement elem } @SuppressWarnings("Convert2MethodRef") - Set getExclusiveResourcesFromProvider(AnnotatedElement element, + Set getExclusiveResourcesFromProvider(Class testClass, Function> providerToResources) { // @formatter:off - return findAnnotation(element, ExclusiveResources.class) + return findAnnotation(testClass, ExclusiveResources.class) .map(annotation -> Stream.of(annotation.value()) .map(providerClass -> ReflectionUtils.newInstance(providerClass)) .map(providerToResources) 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 f427c688b970..1fb7744a3c79 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 @@ -86,8 +86,8 @@ public Set getExclusiveResources() { // @formatter:off Set resourcesFromAnnotation = getExclusiveResourcesFromAnnotation(getTestMethod()); Set resourcesFromProvider = getExclusiveResourcesFromProvider( - getTestMethod(), - provider -> provider.provideForMethod(getTestClass(), getTestMethod()) + getTestClass(), + provider -> provider.provideForMethod(getTestMethod()) ); return Stream.concat(resourcesFromAnnotation.stream(), resourcesFromProvider.stream()).collect(toSet()); diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ExclusiveResourcesProviderOnMethodTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ExclusiveResourcesProviderOnMethodTests.java deleted file mode 100644 index 1b9dd46b9de5..000000000000 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ExclusiveResourcesProviderOnMethodTests.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2015-2024 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.support.hierarchical; - -import static org.junit.platform.engine.support.hierarchical.ExclusiveResourcesProviderOnClassTests.sleep; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.parallel.ExclusiveResources; -import org.junit.jupiter.api.parallel.Execution; -import org.junit.jupiter.api.parallel.ExecutionMode; -import org.junit.jupiter.api.parallel.MyExclusiveResourcesProvider; - -@Execution(ExecutionMode.CONCURRENT) -class ExclusiveResourcesProviderOnMethodTests { - - @ExclusiveResources(MyExclusiveResourcesProvider.class) - @Test - void a() { - sleep(); - } - - @ExclusiveResources(MyExclusiveResourcesProvider.class) - @Test - void b() { - sleep(); - } - - @ExclusiveResources(MyExclusiveResourcesProvider.class) - @Test - void c() { - sleep(); - } - - @ExclusiveResources(MyExclusiveResourcesProvider.class) - @Test - void d() { - sleep(); - } - - @ExclusiveResources(MyExclusiveResourcesProvider.class) - @Test - void e() { - sleep(); - } - - @ExclusiveResources(MyExclusiveResourcesProvider.class) - @Test - void f() { - sleep(); - } - - @ExclusiveResources(MyExclusiveResourcesProvider.class) - @Test - void g() { - sleep(); - } - - @ExclusiveResources(MyExclusiveResourcesProvider.class) - @Test - void h() { - sleep(); - } -} diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ExclusiveResourcesProviderOnNestedClassTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ExclusiveResourcesProviderOnNestedClassTests.java deleted file mode 100644 index bb58543a5f9a..000000000000 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ExclusiveResourcesProviderOnNestedClassTests.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2015-2024 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.support.hierarchical; - -import static org.junit.platform.engine.support.hierarchical.ExclusiveResourcesProviderOnClassTests.sleep; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.parallel.ExclusiveResources; -import org.junit.jupiter.api.parallel.Execution; -import org.junit.jupiter.api.parallel.ExecutionMode; -import org.junit.jupiter.api.parallel.MyExclusiveResourcesProvider; - -@Execution(ExecutionMode.CONCURRENT) -class ExclusiveResourcesProviderOnNestedClassTests { - - @ExclusiveResources(MyExclusiveResourcesProvider.class) - @org.junit.jupiter.api.Nested - class Nested { - - @Test - void a() { - sleep(); - } - - @Test - void b() { - sleep(); - } - - @Test - void c() { - sleep(); - } - - @Test - void d() { - sleep(); - } - - @Test - void e() { - sleep(); - } - - @Test - void f() { - sleep(); - } - - @Test - void g() { - sleep(); - } - - @Test - void h() { - sleep(); - } - } -} diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ExclusiveResourcesProviderOnClassTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/MethodNameBasedExclusiveResourcesProviderTests.java similarity index 54% rename from platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ExclusiveResourcesProviderOnClassTests.java rename to platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/MethodNameBasedExclusiveResourcesProviderTests.java index f3bfae50acdc..d824c0ae3fe2 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ExclusiveResourcesProviderOnClassTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/MethodNameBasedExclusiveResourcesProviderTests.java @@ -14,49 +14,45 @@ import org.junit.jupiter.api.parallel.ExclusiveResources; import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.ExecutionMode; -import org.junit.jupiter.api.parallel.MyExclusiveResourcesProvider; +import org.junit.jupiter.api.parallel.MethodNameBasedExclusiveResourcesProvider; @Execution(ExecutionMode.CONCURRENT) -@ExclusiveResources(MyExclusiveResourcesProvider.class) -class ExclusiveResourcesProviderOnClassTests { +@ExclusiveResources(MethodNameBasedExclusiveResourcesProvider.class) +class MethodNameBasedExclusiveResourcesProviderTests { - @Test - void a() { - sleep(); - } - - @Test - void b() { - sleep(); - } + // 'give_me_lock' tests expected to be executed sequentially to each other + // but free to be executed in parallel with 'no_lock' tests. @Test - void c() { + void give_me_lock_1() { + System.out.println("'give_me_lock_1' started at " + System.currentTimeMillis()); sleep(); } @Test - void d() { + void give_me_lock_2() { + System.out.println("'give_me_lock_2' started at " + System.currentTimeMillis()); sleep(); } @Test - void e() { + void give_me_lock_3() { + System.out.println("'give_me_lock_3' started at " + System.currentTimeMillis()); sleep(); } @Test - void f() { + void no_lock_1() { sleep(); } @Test - void g() { + void no_lock_2() { sleep(); } @Test - void h() { + void no_lock_3() { sleep(); }