Support Cartesian products of environments in MultiEnvTestEngine#7816
Support Cartesian products of environments in MultiEnvTestEngine#7816colan-dremio wants to merge 40 commits intoprojectnessie:mainfrom
Conversation
...t/java/org/projectnessie/tools/compatibility/internal/TestNessieCompatibilityExtensions.java
Show resolved
Hide resolved
...g/multi-env-test-engine/src/main/java/org/projectnessie/junit/engine/MultiEnvTestEngine.java
Outdated
Show resolved
Hide resolved
...env-test-engine/src/main/java/org/projectnessie/junit/engine/MultiEnvTestDescriptorTree.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Thanks for your contribution @colan-dremio !
This is just a preliminary quick review from my side :)
| * </ul> | ||
| * will result in the following IDs: | ||
| * <ul> | ||
| * <li>[engine:nessie-multi-env][segmentA:1][segmentB:1][segmentC:1]</li> |
There was a problem hiding this comment.
I believe MultiEnvTestFilter needs to be updated to check whether all multi-env ID segments are recognised... Currently it checks that any match.
There was a problem hiding this comment.
I don't mind doing this, but I'm unsure if it is necessary. This would couple the cartesian product logic to the filter. The MultiEnvTestEngine is responsible for expanding the environments. Perhaps other types of expansion would be desirable in the future? From the filter's perspective, it seems reasonable to include a test as long as we see at least one correct segment type. This shouldn't happen in practice, so either way is fine with me.
There was a problem hiding this comment.
At the time, I was not correct above about the expansion logic, but now that it is fixed it should hold.
...g/multi-env-test-engine/src/main/java/org/projectnessie/junit/engine/MultiEnvTestEngine.java
Outdated
Show resolved
Hide resolved
...g/multi-env-test-engine/src/main/java/org/projectnessie/junit/engine/MultiEnvTestEngine.java
Outdated
Show resolved
Hide resolved
...-env-test-engine/src/main/java/org/projectnessie/junit/engine/MultiEnvExtensionRegistry.java
Outdated
Show resolved
Hide resolved
...ulti-env-test-engine/src/main/java/org/projectnessie/junit/engine/MultiEnvTestExtension.java
Outdated
Show resolved
Hide resolved
...g/multi-env-test-engine/src/main/java/org/projectnessie/junit/engine/MultiEnvTestFilter.java
Outdated
Show resolved
Hide resolved
# Conflicts: # compatibility/common/src/test/java/org/projectnessie/tools/compatibility/internal/TestNessieCompatibilityExtensions.java
# Conflicts: # testing/multi-env-test-engine/src/test/java/org/projectnessie/junit/engine/TestMultiEnvTestEngine.java
|
Note: I am developing on a Mac and could not validate |
| public MultiEnvDisplayNameGenerator(DisplayNameGenerator delegate, String environmentNames) { | ||
| this.delegate = delegate; | ||
| this.environment = environment; | ||
| this.environmentNames = environmentNames; |
There was a problem hiding this comment.
why rename? The String is opaque in this context, does not convey anything about names, IMHO.
There was a problem hiding this comment.
On review, I meant environmentIds not names, updated accordingly. The goal was to give a bit of clarity into what the value holds. If we want to treat it as opaque, I can rename to suffix instead.
More generally, I've found that there is some ambiguity in naming. This is a multiple environment test engine, what is an environment? Take the example OlderNessieServersExtension (S1, S2) and OlderNessieClientsExtension (C1) which will run:
- S1, C1
- S2, C1
There are a number of concepts that need names:
- Each multienv extension (e.g. OlderNessieServersExtension)
- Each segement type (e.g. nessie-server-version)
- Each "environment id" within an extension (e.g. S1 and S2 individually)
- Each unique combination of environment ids on a test execution (e.g. [S1, C1])
If we go from the name here, it seems #4 is meant to be "environment"
Possible names to make this work:
- Dimension
- Dimension type
- Dimension element
- Environment
e.g.
public interface MultiEnvTestExtension extends Extension {
String dimensionType();
List<String> allDimensionElements(ConfigurationParameters configuration);
default int dimensionPriority() {
return 0;
}
}
Thoughts?
There was a problem hiding this comment.
This particular class is meant to make multi-env testa distinguishable in tools that do not understand Junit5 UniqueId (i.e. those that deal with test class/method names only). environmentIds works in this case, but I still think the rename is unnecessary in this case as the old environment works just as well :)
There was a problem hiding this comment.
1. Dimention - SGTM (but let's wait a bit with renaming :) )
2. Dimention type - I prefer segment (current) as it related to JUnit5 ID segments.
3. Dimention elements - Maybe Category? (in the statistical sense)
There was a problem hiding this comment.
Keeping environment as # 4 is fine with me. I don't think "category" should be used for elements, "category" is synonymous with dimension type / segment type. Also, I don't think we should use "segment" alone because a UniqueId segment is the pair of segment type + segment value.
It looks like we should choose
- One of (dimension | segment | category) for # 2
- Whether or not to use "type" as a suffix for # 2. I'd avoid "segment" alone, but could see "category" or "category type" working fine.
- One of (value | element | ID) for # 3
My vote at the moment is "dimension", "type", and "value":
public @interface MultiEnvDimensionType {}
public interface MultiEnvTestExtension extends Extension {
List<String> allDimensionValues(ConfigurationParameters configuration);
int segmentPriority()
}
For now, I've reverted these back to environment and will see what the rename would look like in practice.
There was a problem hiding this comment.
Proposed naming: colan-dremio@e4625b7
There are some subtle advantages here. For example in this block, it can be ambiguous about whether currentSegmentTypes/Values is referring to MultiEnv segments or all JUnit segments (engine, class, nested-class, method too). I had considered currentMultiEnvSegmentTypes/Values to clarify this. Now that it is currentDimensionTypes/Values, the ambiguity is also resolved.
...g/multi-env-test-engine/src/main/java/org/projectnessie/junit/engine/MultiEnvTestFilter.java
Outdated
Show resolved
Hide resolved
...lti-env-test-engine/src/test/java/org/projectnessie/junit/engine/TestMultiEnvTestEngine.java
Outdated
Show resolved
Hide resolved
...v-test-engine/src/main/java/org/projectnessie/junit/engine/MultiEnvJupiterConfiguration.java
Outdated
Show resolved
Hide resolved
| List<TestDescriptor> newLeafNodes = new ArrayList<>(); | ||
| for (TestDescriptor parentNode : latestLeafNodes) { | ||
| for (String environmentId : | ||
| multiEnvTestExtension.allEnvironmentIds(configurationParameters)) { |
There was a problem hiding this comment.
TBH, I find this method of constructing a Cartesian product of environments a bit obscure. Why not simply have nested loops in one method (without calling out to this helper class)?
There was a problem hiding this comment.
I've moved the tree construction logic directly into the engine implementation as part of fixing the updated cartesian test. The main annoyance is that the tree of nodes (represented by recursive child references) sits independently of the UniqueIds of those nodes. My new implementation still maintains a cache of known nodes to keep lookup simple, but should be simpler overall.
...g/multi-env-test-engine/src/main/java/org/projectnessie/junit/engine/MultiEnvTestFilter.java
Show resolved
Hide resolved
# Conflicts: # testing/multi-env-test-engine/src/main/java/org/projectnessie/junit/engine/MultiEnvTestDescriptorTree.java
dimas-b
left a comment
There was a problem hiding this comment.
Only a partial review this time, but I think we're getting close to the end :)
...t/java/org/projectnessie/tools/compatibility/internal/TestNessieCompatibilityExtensions.java
Outdated
Show resolved
Hide resolved
| * Interface for JUnit5 test extensions that require running the same suite of tests in multiple | ||
| * executions environments. For example, running the same tests for multiple versions of a Nessie | ||
| * Client. | ||
| * Client.<br> |
There was a problem hiding this comment.
Maybe just <p>? cf.
| .filter(MultiEnvTestExtension.class::isAssignableFrom) | ||
| .flatMap(registry::stream); | ||
| return r; | ||
| return findMultiEnvTestExtensionsOn(testClass).flatMap(registry::stream); |
There was a problem hiding this comment.
Maybe use it in registerExtensions too?
| @@ -51,19 +51,6 @@ public Stream<MultiEnvTestExtension> stream() { | |||
| } | |||
|
|
|||
| public Stream<? extends MultiEnvTestExtension> stream(Class<?> testClass) { | |||
There was a problem hiding this comment.
stream() (no args) is unused now.
There was a problem hiding this comment.
Weird, my IDE seems to be confused about this one. Removed.
| } | ||
|
|
||
| /** Immutable key of segment types for the intermediate cartesian product tree. */ | ||
| private static class SegmentTypes { |
There was a problem hiding this comment.
Why not @Value.Immutable? It would be possible to add default helper methods for .root() and .append(String child) even if SegmentTypes were an interface.
| putTestIntoParent( | ||
| testDescriptor, nodeAtCurrentPosition, environmentNames, discoveryRequest); |
There was a problem hiding this comment.
I think it might still be preferable to use the old resolveSelectors(...) method... How does the new code behave when (e.g. IDE) selects only one test method for execution? For example, run the whole test suite, but then select only one test in one particular environment for debugging in IntelliJ.
In this PR in my env. that will run the test method in all environments, before the PR, only in one env (as it should).
There was a problem hiding this comment.
Added this back. It had some interesting side effects where resolution would discover children that do not apply. With some post-filtering, it looks like this works better.
Added tests to cover these scenarios.
…esn't select too many things
Currently,
MultiEnvTestEngineonly supports a singleMultiEnvTestExtensionat a time. This adds support for multipleMultiEnvTestExtensions. It will perform a Cartesian product of the registered environment IDs.For example, it would become possible to test multiple Nessie client versions with multiple Nessie server versions through a combination of
OlderNessieClientsExtensionandOlderNessieServersExtension. Withnessie.versionsproperty set to1,2, the following test environments would run:This is also enables downstream projects to use their own implementations of
MultiEnvTestExtension. For example, a downstream project could test multiple Nessie versions against multiple types of object storage.Future work: Does not yet support complex
@Nestedtests. Two tests are added but@Disabledfor future development, seeonlyNestedTestandcartesianNestedTest.