Skip to content

Commit

Permalink
Move '@ResourceLocksFrom' functionality to '@ResourceLock'.
Browse files Browse the repository at this point in the history
  • Loading branch information
VladimirDmitrienko committed Aug 15, 2024
1 parent 1f8a6d0 commit e1b5473
Show file tree
Hide file tree
Showing 15 changed files with 275 additions and 175 deletions.
2 changes: 1 addition & 1 deletion documentation/src/docs/asciidoc/link-attributes.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ endif::[]
:Execution: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/parallel/Execution.html[@Execution]
:Isolated: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/parallel/Isolated.html[@Isolated]
:ResourceLock: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/parallel/ResourceLock.html[@ResourceLock]
:ResourceLocksFrom: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/parallel/ResourceLocksFrom.html[@ResourceLocksFrom]
:ResourceLocksProvider: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/parallel/ResourceLocksProvider.html[ResourceLocksProvider]
:Resources: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/parallel/Resources.html[Resources]
// Jupiter Extension APIs
:extension-api-package: {javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/package-summary.html[org.junit.jupiter.api.extension]
Expand Down
47 changes: 28 additions & 19 deletions documentation/src/docs/asciidoc/user-guide/writing-tests.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2897,23 +2897,32 @@ strategy

In addition to controlling the execution mode using the `{Execution}` annotation, JUnit
Jupiter provides another annotation-based declarative synchronization mechanism. The
`{ResourceLock}` and `{ResourceLocksFrom}` annotations allows you to declare that
a test class or method uses a specific shared resource that requires synchronized access
to ensure reliable test execution. The shared resource is identified by a unique name
which is a `String`. The name can be user-defined or one of the predefined constants
in `{Resources}`: `SYSTEM_PROPERTIES`, `SYSTEM_OUT`, `SYSTEM_ERR`, `LOCALE`, or `TIME_ZONE`.
`{ResourceLock}` annotation allows you to declare that a test class or method uses a
specific shared resource that requires synchronized access to ensure reliable test
execution. The shared resource is identified by a unique name which is a `String`.
The name can be user-defined or one of the predefined constants in `{Resources}`:
`SYSTEM_PROPERTIES`, `SYSTEM_OUT`, `SYSTEM_ERR`, `LOCALE`, or `TIME_ZONE`.

If the tests in the following examples were run in parallel _without_ the use of
`{ResourceLock}` or `{ResourceLocksFrom}`, they would be _flaky_. Sometimes they would pass,
and at other times they would fail due to the inherent race condition of writing
and then reading the same JVM System Property.
Since Junit Jupiter 5.12 `{ResourceLock}` annotation has 'providers' attribute
which accepts an array of one or more classes implementing `{ResourceLocksProvider}`
interface. This interface allows to add shared resources in runtime.

Note that resources declared "statically" with `{ResourceLock}` annotation
are combined with resources added "dynamically" with `{ResourceLocksProvider}`
interface.

When access to shared resources is declared using the `{ResourceLock}` or `{ResourceLocksFrom}`
annotation, the JUnit Jupiter engine uses this information to ensure that no conflicting
tests are run in parallel. This guarantee extends to lifecycle methods of a test class or method.
For example, if a test method is annotated with a `{ResourceLock}` annotation, the "lock" will
be acquired before any `@BeforeEach` methods are executed and released after all
`@AfterEach` methods have been executed.
If the tests in the following examples were run in parallel _without_ the use of
`{ResourceLock}` or `{ResourceLocksProvider}`, they would be _flaky_.
Sometimes they would pass, and at other times they would fail due to
the inherent race condition of writing and then reading the same JVM System Property.

When access to shared resources is declared using the `{ResourceLock}` annotation
or added with `{ResourceLocksProvider}` interface, the JUnit Jupiter engine uses
this information to ensure that no conflicting tests are run in parallel.
This guarantee extends to lifecycle methods of a test class or method.
For example, if a test method is annotated with a `{ResourceLock}` annotation,
the "lock" will be acquired before any `@BeforeEach` methods are executed
and released after all `@AfterEach` methods have been executed.

[NOTE]
.Running tests in isolation
Expand All @@ -2930,15 +2939,15 @@ parallel with each other but not while any other test that requires `READ_WRITE`
to the same shared resource is running.

[source,java]
.Declaring shared resources with `{ResourceLock}` annotation
.Declaring shared resources "statically" with `{ResourceLock}` annotation
----
include::{testDir}/example/SharedResourcesWithResourceLockAnnotationDemo.java[tags=user_guide]
include::{testDir}/example/sharedresources/StaticSharedResourcesDemo.java[tags=user_guide]
----

[source,java]
.Declaring shared resources provider with `{ResourceLocksFrom}` annotation
.Adding shared resources "dynamically" with `{ResourceLocksProvider}` interface
----
include::{testDir}/example/SharedResourcesWithResourceLocksFromAnnotationDemo.java[tags=user_guide]
include::{testDir}/example/sharedresources/DynamicSharedResourcesDemo.java[tags=user_guide]
----


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* https://www.eclipse.org/legal/epl-v20.html
*/

package example;
package example.sharedresources;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
Expand All @@ -27,13 +27,13 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ResourceAccessMode;
import org.junit.jupiter.api.parallel.ResourceLocksFrom;
import org.junit.jupiter.api.parallel.ResourceLock;
import org.junit.jupiter.api.parallel.ResourceLocksProvider;

// tag::user_guide[]
@Execution(CONCURRENT)
@ResourceLocksFrom(SharedResourcesWithResourceLocksFromAnnotationDemo.Provider.class)
class SharedResourcesWithResourceLocksFromAnnotationDemo {
@ResourceLock(providers = DynamicSharedResourcesDemo.Provider.class)
class DynamicSharedResourcesDemo {

private Properties backup;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* https://www.eclipse.org/legal/epl-v20.html
*/

package example;
package example.sharedresources;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
Expand All @@ -27,7 +27,7 @@

// tag::user_guide[]
@Execution(CONCURRENT)
class SharedResourcesWithResourceLockAnnotationDemo {
class StaticSharedResourcesDemo {

private Properties backup;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
*
* @see Isolated
* @see ResourceLock
* @see ResourceLocksFrom
* @since 5.3
*/
@API(status = STABLE, since = "5.10")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
* @since 5.7
* @see ExecutionMode
* @see ResourceLock
* @see ResourceLocksFrom
*/
@API(status = STABLE, since = "5.10")
@Retention(RetentionPolicy.RUNTIME)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

package org.junit.jupiter.api.parallel;

import static org.apiguardian.api.API.Status.EXPERIMENTAL;
import static org.apiguardian.api.API.Status.STABLE;

import java.lang.annotation.ElementType;
Expand Down Expand Up @@ -48,11 +49,16 @@
* <p>Since JUnit Jupiter 5.4, this annotation is {@linkplain Inherited inherited}
* within class hierarchies.
*
* <p>Since JUnit Jupiter 5.12, this annotation supports adding shared resources
* in runtime via {@link ResourceLock#providers}.
*
* <p>Resources declared "statically" using {@code @ResourceLock(value, mode)}
* are combined with "dynamic" resources added via {@link ResourceLocksProvider.Lock}.
*
* @see Isolated
* @see Resources
* @see ResourceAccessMode
* @see ResourceLocks
* @see ResourceLocksFrom
* @see ResourceLocksProvider
* @see ResourceLocksProvider.Lock
* @since 5.3
Expand All @@ -67,17 +73,32 @@
/**
* The resource key.
*
* <p>Defaults to an empty string.
*
* @see Resources
* @see ResourceLocksProvider.Lock#getKey()
*/
String value();
String value() default "";

/**
* The resource access mode.
*
* <p>Defaults to {@link ResourceAccessMode#READ_WRITE READ_WRITE}.
*
* @see ResourceAccessMode
* @see ResourceLocksProvider.Lock#getAccessMode()
*/
ResourceAccessMode mode() default ResourceAccessMode.READ_WRITE;

/**
* An array of one or more classes implementing {@link ResourceLocksProvider}.
*
* <p>Defaults to an empty array.
*
* @see ResourceLocksProvider.Lock
* @since 5.12
*/
@API(status = EXPERIMENTAL, since = "5.12")
Class<? extends ResourceLocksProvider>[] providers() default {};

}

This file was deleted.

Loading

0 comments on commit e1b5473

Please sign in to comment.