-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ExclusiveResource programmatically #2677
Comments
We could add a new extension point for this: interface ExclusiveResourceProvider extends Extension {
default Set<ExclusiveResource> provideExclusiveResourcesForClass(Class<?> testClass, Set<ExclusiveResource> declaredResources) {
return declaredResources;
};
default Set<ExclusiveResource> provideExclusiveResourcesForNestedClass(Class<?> nestedClass, Set<ExclusiveResource> declaredResources) {
return declaredResources;
};
default Set<ExclusiveResource> provideExclusiveResourcesForMethod(Class<?> testClass, Method testMethod, Set<ExclusiveResource> declaredResources) {
return declaredResources;
};
}
interface ExclusiveResource {
String getKey();
ResourceAccessMode getAccessMode();
static ExclusiveResource of(String key);
static ExclusiveResource of(String key, ResourceAccessMode accessMode);
} |
This feature might be quite useful for my PR on junit-pioneer to allow "resources" - arbitrary objects - to be instantiated and shared amongst tests declaratively. Specifically, if each "resource" were to have its own The proposed API leaves me with one question, though: if my extension were to implement |
It would have to be called immediately before starting execution on the engine level. |
Okay, thank you! I wasn't sure at first if this would work for me, since my extension's resources are created at test runtime. But I think it could work now, as all shared resources in my extension are declared with So I'm happy with the proposed API. 👍 |
@marcphilipp I'd love to! However I still have junit-pioneer/junit-pioneer#348 to finish, so I'm happy for anyone else to pick this up before me. |
For clarity, my JUnit Pioneer work is not actually blocked on this feature, as I've found an alternative solution to programmatic |
@marcphilipp A follow-up query from me: do you know if JUnit Jupiter already ensures that If not, then I, or whoever picks this up, will need to ensure the locks are sorted when being moved from |
They are sorted here: Line 61 in 6a47ae1
|
@marcphilipp Great, thank you for answering so quickly. 👍 |
@marcphilipp I'd love it if you assigned this to me, as I've managed to park my junit-pioneer work and find time to start looking at this. |
@jbduncan Sorry it took 6 days, but you're now assigned. |
Sorry for the silence on this! I was a bit lost looking through the JUnit 5 codebase when I looked at this issue last year. But now that my JUnit Pioneer PR that would have used this feature is finally done, I'll see if I can find the time to revisit this issue later this week, and write down anything I need help with. :) |
@marcphilipp I have a few follow-up questions which I was hoping you could help me with. For the proposed
|
I've pushed my ongoing spike to #3096 for the sake of transparency. :) |
@marcphilipp I'm feeling a bit stuck now, and I was wondering if you could help me. It looks like Alternatively, would you be up to discussing this remotely some time? If so, feel free to contact me on my GitHub email address. |
@marcphilipp I don't suppose you've had the time to look at my last comment recently, have you? 🙂 |
@jbduncan Sorry, I had fallen quite behind in reading my GitHub notifications. 😬
I'm not sure I fully understand the question. |
Fair enough. There are two options coming in my mind.
public @interface ResourceLocks {
Class<? extends ResourceLocksProvider> provider() default EmptyResourceLocksProvider.class;
// or
Class<? extends ResourceLocksProvider>[] providers() default {};
}
|
That would break backward compatibility.
Let's go with that. Renaming it as part of the PR should be easy enough if we come up with a better name. |
Did you mean adding another attribute to the container annotation? If so, you may be right but I still think it would be confusing to appropriate it in this way. |
Got it, thank you 🙂 |
Hi @marcphilipp I've updated the PR and now it's ready for review. Btw, I've set |
Hi @marcphilipp |
Sorry for the delay! I was on vacation. I should be able to get to it this week. 🤞 |
@marcphilipp Thanks for the update 👍 |
Reopening to track polishing work |
Instead of looking up annotations on the declaring and all enclosing classes for each test method, each `ResourceLockAware` test descriptor now delegates to the `ExclusiveResourceCollector` of its ancestors which cache the class-level annotations and `ResourceLocksProvider` instances. Resolves #2677.
Thanks for your contribution, @VladimirDmitrienko! 👍 |
little late to the party here, looking at the new experimental api for this: https://junit.org/junit5/docs/snapshot/api/org.junit.jupiter.api/org/junit/jupiter/api/parallel/ResourceLocksProvider.html was it purposeful to exclude the test template / parameterised tests? seems you can only provide dynamically for class/method and nested class and not a templated or parameterised test which is what I was hoping for for my usecase? In my scenario i want to use a property of the parameterised test input to define the resource lock key for a resource shared amongst a subset of tests.. |
Actually Let's consider such an example: @ParameterizedTest
@ValueSource(strings = {"banana", "apple", "orange"})
void fruitTest(String value) {
// ...
}
static class Provider implements ResourceLocksProvider {
@Override
public Set<Lock> provideForMethod(Class<?> testClass, Method testMethod) {
return Set.of(new Lock("fruitKey"));
}
} The same lock applies to each invocation: It's impossible to define different locks based on the arguments, for example, setting the same lock for "banana" and "apple" but a different one for "orange". The reason is that locks are collected before test execution started: we first collect the locks and only then pass them to engine that executes tests. Assigning locks depending on arguments could be achieved by splitting the test: @ParameterizedTest
@ValueSource(strings = {"banana", "apple"})
void bananaAndAppleTest(String value) {
// ...
}
@ParameterizedTest
@ValueSource(strings = {"orange"})
void orangeTest(String value) {
// ...
}
static class Provider implements ResourceLocksProvider {
@Override
public Set<Lock> provideForMethod(Class<?> testClass, Method testMethod) {
if (testMethod.getName().equals("bananaAndAppleTest")) {
return Set.of(new Lock("bananaAndAppleKey"));
}
else if (testMethod.getName().equals("orangeTest")) {
return Set.of(new Lock("orangeKey"));
}
return Set.of();
}
} |
thanks for the detailed explanation! the approach makes sense but is not possible for the shape of our problem unfortunately, splitting the tests based on their locks isn't really feasible unfortunately so i'll have to think about a different way to achieve it. 👍 |
I have several test classes, each with multiple test methods inside. Some of those classes are injected with a parameter, and when running classes in parallel those classes should not run parallel. If I understand correctly, the
@ResourceLock
annotation can help here, but it would be very inconvenient to manually add that annotation to hundereds of tests. Ideally a extension could take a look at the class, see that it has this parameter and somehow add the ExclusiveResource to it.My JUnit execution parameters:
The text was updated successfully, but these errors were encountered: