New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduced @ResourceLock(target = SELF | CHILDREN)
#3220
base: main
Are you sure you want to change the base?
Changes from 6 commits
43605a2
e6801ff
b559ee5
a23cae2
95b323a
9fe9dca
baccd68
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
/* | ||
* Copyright 2015-2023 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 example; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertNull; | ||
import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; | ||
import static org.junit.jupiter.api.parallel.ResourceAccessMode.READ; | ||
import static org.junit.jupiter.api.parallel.ResourceAccessMode.READ_WRITE; | ||
import static org.junit.jupiter.api.parallel.Resources.SYSTEM_PROPERTIES; | ||
import static org.junit.jupiter.api.parallel.Resources.TIME_ZONE; | ||
|
||
import java.util.Properties; | ||
import java.util.TimeZone; | ||
|
||
import org.junit.jupiter.api.AfterEach; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.parallel.Execution; | ||
import org.junit.jupiter.api.parallel.ResourceLock; | ||
import org.junit.jupiter.api.parallel.ResourceLockTarget; | ||
|
||
// tag::user_guide[] | ||
@Execution(CONCURRENT) | ||
@ResourceLock(value = TIME_ZONE, mode = READ, target = ResourceLockTarget.CHILDREN) | ||
class SharedResourcesChildrenTargetDemo { | ||
|
||
private Properties backup; | ||
|
||
@BeforeEach | ||
void backup() { | ||
backup = new Properties(); | ||
backup.putAll(System.getProperties()); | ||
} | ||
|
||
@AfterEach | ||
void restore() { | ||
System.setProperties(backup); | ||
} | ||
|
||
@Test | ||
@ResourceLock(value = SYSTEM_PROPERTIES, mode = READ) | ||
void usePropertiesAndTimeZoneWithoutModification() { | ||
assertNull(System.getProperty("my.prop")); | ||
} | ||
|
||
@Test | ||
@ResourceLock(value = SYSTEM_PROPERTIES, mode = READ) | ||
void usePropertiesAndTimeZoneWithoutModificationAgain() { | ||
assertNull(System.getProperty("my.prop")); | ||
} | ||
|
||
@Test | ||
@ResourceLock(value = SYSTEM_PROPERTIES, mode = READ_WRITE) | ||
void canSetCustomPropertyToTimeZone() { | ||
String timezone = TimeZone.getDefault().getDisplayName(); | ||
System.setProperty("my.timezone", timezone); | ||
assertEquals(timezone, System.getProperty("my.timezone")); | ||
} | ||
|
||
} | ||
// end::user_guide[] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/* | ||
* Copyright 2015-2023 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.EXPERIMENTAL; | ||
|
||
import org.apiguardian.api.API; | ||
|
||
/** | ||
* Indicates the target of a {@link ResourceLock}. | ||
* | ||
* @since 5.10 | ||
* @see ResourceLock | ||
*/ | ||
@API(status = EXPERIMENTAL, since = "5.10") | ||
public enum ResourceLockTarget { | ||
|
||
/** | ||
* Point to the test descriptor itself | ||
*/ | ||
SELF, | ||
|
||
/** | ||
* Skip the test descriptor itself and apply annotation {@link ResourceLock} to all its children | ||
*/ | ||
CHILDREN | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,6 @@ | |
|
||
import static java.util.stream.Collectors.collectingAndThen; | ||
import static java.util.stream.Collectors.toCollection; | ||
import static java.util.stream.Collectors.toSet; | ||
import static org.apiguardian.api.API.Status.INTERNAL; | ||
import static org.junit.jupiter.engine.descriptor.DisplayNameUtils.determineDisplayName; | ||
import static org.junit.platform.commons.util.AnnotationUtils.findAnnotation; | ||
|
@@ -25,6 +24,7 @@ | |
import java.util.Optional; | ||
import java.util.Set; | ||
import java.util.function.Supplier; | ||
import java.util.stream.Collectors; | ||
|
||
import org.apiguardian.api.API; | ||
import org.junit.jupiter.api.Tag; | ||
|
@@ -33,6 +33,7 @@ | |
import org.junit.jupiter.api.parallel.Execution; | ||
import org.junit.jupiter.api.parallel.ResourceAccessMode; | ||
import org.junit.jupiter.api.parallel.ResourceLock; | ||
import org.junit.jupiter.api.parallel.ResourceLockTarget; | ||
import org.junit.jupiter.engine.config.JupiterConfiguration; | ||
import org.junit.jupiter.engine.execution.ConditionEvaluator; | ||
import org.junit.jupiter.engine.execution.JupiterEngineExecutionContext; | ||
|
@@ -180,11 +181,31 @@ public static ExecutionMode toExecutionMode(org.junit.jupiter.api.parallel.Execu | |
throw new JUnitException("Unknown ExecutionMode: " + mode); | ||
} | ||
|
||
Set<ExclusiveResource> getExclusiveResourcesFromAnnotation(AnnotatedElement element) { | ||
protected List<ResourceLock> getResourceLocks() { | ||
return Collections.emptyList(); | ||
} | ||
|
||
protected Set<ExclusiveResource> collectExclusiveResourcesFromHierarchy() { | ||
Set<ExclusiveResource> resources = mapResourceLocksForTarget(getResourceLocks(), ResourceLockTarget.SELF); | ||
|
||
Optional<TestDescriptor> nextParent = getParent(); | ||
while (nextParent.isPresent()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIUC There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Is there any reason for this? Is it harmful in someway for it to not be just direct children? e.g. in our project our tests inherit from base classes scattered throughout our packages and those base classes tend to inherit from a single source parent. If I have a resource that I want read locked for everything (main server component for example) it makes more sense to me to have the lock specified in one place instead of having to remember to add it to all the base classes There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Squibbles This is unrelated to inheritance in class hierarchies. The |
||
TestDescriptor testDescriptor = nextParent.get(); | ||
if (testDescriptor instanceof JupiterTestDescriptor) { | ||
List<ResourceLock> parentLocks = ((JupiterTestDescriptor) testDescriptor).getResourceLocks(); | ||
resources.addAll(mapResourceLocksForTarget(parentLocks, ResourceLockTarget.CHILDREN)); | ||
} | ||
nextParent = testDescriptor.getParent(); | ||
} | ||
return resources; | ||
} | ||
|
||
private Set<ExclusiveResource> mapResourceLocksForTarget(List<ResourceLock> locks, ResourceLockTarget target) { | ||
// @formatter:off | ||
return findRepeatableAnnotations(element, ResourceLock.class).stream() | ||
return locks.stream() | ||
.filter(lock -> lock.target() == target) | ||
.map(resource -> new ExclusiveResource(resource.value(), toLockMode(resource.mode()))) | ||
.collect(toSet()); | ||
.collect(Collectors.toSet()); | ||
// @formatter:on | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if a class has a
@ResourceLock
annotation with targetCHILDREN
and a method has one for the same resource? Does the one from the method override the one from the class? This should be documented here.