-
Notifications
You must be signed in to change notification settings - Fork 24.8k
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
Fix test reproducability in AbstractBuilderTestCase setup #32403
Fix test reproducability in AbstractBuilderTestCase setup #32403
Conversation
Currently AbstractBuilderTestCase generates certain random values in its `beforeTest()` method annotated with @before only the first time that a test method in the suite is run. This changes the values of subsequent random values and has the effect that when running single methods from a test suite with "-Dtests.method=*", the random values it sees are different from when the same test method is run as part of the whole test suite. This makes it harder to use the reproduction lines logged on failure. This changes the above method so that `beforeTest` will do the same call to the random value source each time a test method is run, so reproduction by running just one method is possible again. Closes elastic#32400
Pinging @elastic/es-search-aggs |
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.
Good catch @cbuescher , I left a comment
// creating ServiceHolder just once. The other times are necessary so that further random | ||
// generation stays the same for all test that is run, otherwise reproducability breaks | ||
Settings indexSettings = createTestIndexSettings(); | ||
long now = randomNonNegativeLong(); |
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.
can we initialize indexVersionCreated
and now
in the BeforeClass as static members like we do for nodeSetttings
and index
?
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.
Good point, I will change this for now
, I don't think we need different values for now for different test cases. For indexVersionCreated
I find this to be more difficult. We have at least three tests (ParentIdQueryBuilderTests, HasChildQueryBuilderTests, HasParentQueryBuilderTests) overwriting createTestIndexSettings to create a specialized version setting (it seems to be restricted to CURRENT). I'm not sure we can use this mechanism of overwriting default versions in specific tests if the call this in a static context in BeforeClass. I might have missed something though, let me know what you think.
Conflicts: test/framework/src/main/java/org/elasticsearch/test/AbstractBuilderTestCase.java
@jimczi thanks for the review, I updated the PR by adressing one of your suggestions, left a question for the other. Let me know what you think about it. |
// the two following ranomized parameters are drawn every run, even though we use them in | ||
// creating ServiceHolder just once. The other times are necessary so that further random | ||
// generation stays the same for all test that is run, otherwise reproducability breaks | ||
Settings indexSettings = createTestIndexSettings(); |
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.
The proposed fix is ok, but I wonder if the following wouldn't be better:
@Before
public void beforeTest() throws Exception {
if (serviceHolder == null) {
assert serviceHolderWithNoType == null;
long masterSeed = SeedUtils.parseSeed(RandomizedTest.getContext().getRunnerSeedAsString());
RandomizedTest.getContext().runWithPrivateRandomness(masterSeed, (Callable<Void>) () -> {
serviceHolder = new ServiceHolder(nodeSettings, indexSettings(), getPlugins(), AbstractBuilderTestCase.this, true);
serviceHolderWithNoType = new ServiceHolder(nodeSettings, indexSettings(), getPlugins(), AbstractBuilderTestCase.this, false);
return null;
});
}
serviceHolder.clientInvocationHandler.delegate = this;
serviceHolderWithNoType.clientInvocationHandler.delegate = this;
}
The idea is to execute this conditional initialization step under the suite randomness rather than the method randomness. If we don't want to parse the suite seed back from a string we could also generate our own suite seed, the important bit is that we generate it in the beforeClass which is executed under the suite randomness (master seed). I find that this way what we are doing is clearer. The proposed fix is good but can easily be reverted by mistake by just moving lines around.
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.
Great suggestion, I added this.
I left one comment, I think it would also be good to add a test for this but it's quite tricky to do and I don't have a good idea around that. I had a quick look at some of our base test classes to see where we do similar things (conditionally initialize static variables in before methods, and generate random values as part of such init methods) and I think that |
@javanna thanks for the suggestion, I added it in another commit, together with some comment summing up the resoning for it so we don't forget why we are doing this here. Can you take another look? |
: VersionUtils.randomVersionBetween(random(), Version.V_6_0_0, Version.CURRENT); | ||
return Settings.builder() | ||
.put(IndexMetaData.SETTING_VERSION_CREATED, indexVersionCreated) | ||
.build(); | ||
} | ||
|
||
protected IndexSettings indexSettings() { |
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.
could this be static?
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.
LGTM thanks @cbuescher !
Currently AbstractBuilderTestCase generates certain random values in its `beforeTest()` method annotated with @before only the first time that a test method in the suite is run while initializing the serviceHolder that we use for the rest of the test. This changes the values of subsequent random values and has the effect that when running single methods from a test suite with "-Dtests.method=*", the random values it sees are different from when the same test method is run as part of the whole test suite. This makes it hard to use the reproduction lines logged on failure. This change runs the inialization of the serviceHolder and the randomization connected to it using the test runners master seed, so reproduction by running just one method is possible again. Closes #32400
Currently AbstractBuilderTestCase generates certain random values in its `beforeTest()` method annotated with @before only the first time that a test method in the suite is run while initializing the serviceHolder that we use for the rest of the test. This changes the values of subsequent random values and has the effect that when running single methods from a test suite with "-Dtests.method=*", the random values it sees are different from when the same test method is run as part of the whole test suite. This makes it hard to use the reproduction lines logged on failure. This change runs the inialization of the serviceHolder and the randomization connected to it using the test runners master seed, so reproduction by running just one method is possible again. Closes #32400
…listeners * elastic/master: (58 commits) [ML] Partition-wise maximum scores (elastic#32748) [DOCS] XContentBuilder#bytes method removed, using BytesReference.bytes(docBuilder) (elastic#32771) HLRC: migration get assistance API (elastic#32744) Add a task to run forbiddenapis using cli (elastic#32076) [Kerberos] Add debug log statement for exceptions (elastic#32663) Make x-pack core pull transport-nio (elastic#32757) Painless: Clean Up Whitelist Names (elastic#32791) Cat apis: Fix index creation time to use strict date format (elastic#32510) Clear Job#finished_time when it is opened (elastic#32605) (elastic#32755) Test: Only sniff host metadata for node_selectors (elastic#32750) Update scripted metric docs to use `state` variable (elastic#32695) Painless: Clean up PainlessCast (elastic#32754) [TEST] Certificate NONE not allowed in FIPS JVM (elastic#32753) [ML] Refactor ProcessCtrl into Autodetect and Normalizer builders (elastic#32720) Access build tools resources (elastic#32201) Tests: Disable rolling upgrade tests with system key on fips JVM (elastic#32775) HLRC: Ban LoggingDeprecationHandler (elastic#32756) Fix test reproducability in AbstractBuilderTestCase setup (elastic#32403) Only require java<version>_home env var if needed Tests: Muted ScriptDocValuesDatesTests.testJodaTimeBwc ...
Currently AbstractBuilderTestCase generates certain random values in its
beforeTest()
method annotated with @before only the first time that a testmethod in the suite is run. This changes the values of subsequent random values
and has the effect that when running single methods from a test suite with
"-Dtests.method=*", the random values it sees are different from when the same
test method is run as part of the whole test suite. This makes it harder to use
the reproduction lines logged on failure.
This changes the above method so that
beforeTest
will do the same call to therandom value source each time a test method is run, so reproduction by running
just one method is possible again.
Closes #32400