-
Notifications
You must be signed in to change notification settings - Fork 38.7k
tests: Make descriptor tests deterministic #16570
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
tests: Make descriptor tests deterministic #16570
Conversation
|
Woops, lint failed.. |
|
@davereikher The linter is just complaining about trailing whitespace: This diff appears to have added new lines with trailing whitespace.
The following changes were suspected:
diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp
@@ -55,0 +63 @@ std::string MaybeUseHInsteadOfApostrophy(std::string ret)
+
@@ -61 +69,2 @@ const std::set<std::vector<uint32_t>> ONLY_EMPTY{{}};
+void DoCheck(const std::string& prv, const std::string& pub, int flags, const std::vector<std::vector<std::string>>& scripts, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY,
^---- failure generated from test/lint/lint-whitespace.shYou should be able to run the |
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsNo conflicts as of last run. |
|
The |
achow101
left a comment
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.
Concept ACK. I've left a few comments.
I think it makes sense to test the parsing of both the ' and h key origin forms for all descriptors.
src/script/descriptor.cpp
Outdated
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.
This comment shouldn't be moved.
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.
I'm not sure I understand. It's not quite clear in the diff, but the // Checksum comment was not moved. It remains after namespace {. The only comment that was moved is the one above PolyMod, describing the operation of that function, along with the function.
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.
In other words: The function does not need to move, only the namespace {
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.
Thanks, fixed in commit 6819fbd741891081b2679e4b763bb88dc997ffe7. Is there a way to re-run AppVeyor? Looks like it timed out.
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.
You can re-run the ci by squashing your commits according to https://github.com/bitcoin/bitcoin/blob/master/CONTRIBUTING.md#squashing-commits
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.
Ok, done in commit 0fc46f5b9262f016c42d2abe68d5125833f55416.
src/test/descriptor_tests.cpp
Outdated
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.
Unnecessary whitespace change.
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.
Thanks, fixed in commit d3e2d08a3be92cecb50c89c69871e61e0618e10b.
src/test/descriptor_tests.cpp
Outdated
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.
I think it would make more sense to largely leave this function untouched and instead do the ' to h change in your new Check function below. The only change here would be to remove MaybeUseHInsteadOfApostrophy
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.
I agree and I did it that way initially but then, for example, this check failed:
std::string pub1 = parse_priv->ToString();
BOOST_CHECK(EqualDescriptor(pub, pub1));
since Descriptor::ToString returns a descriptor formatted with ', while pub has h, if I do it the way you suggest. I had to work around this by passing pub and prv without modification and introducing a boolean argument of whether to swap ' with h only when passing them to the Parse function.
6819fbd to
0fc46f5
Compare
src/test/descriptor_tests.cpp
Outdated
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.
This is actually backwards. When replace_apostrophe_with_h is false, we're swapping. I think it's supposed to be the other way 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.
Thanks, you're absolutely right. Fixed in a8f49b7d53d33aeba8bd0965c21878795b36f99b.
meshcollider
left a comment
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.
Concept ACK
promag
left a comment
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.
Concept ACK.
PolyMod linkage changed, make it static?
src/test/descriptor_tests.cpp
Outdated
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.
nit, static function.
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.
It's in an anonymous namespace, or am I missing something?
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.
True, sorry.
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.
But make it std::string UseHInsteadOfApostrophe(const std::string& ret). Also could avoid (rename is unrelated here).ret argument, sounds like it's an output/return argument.
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.
I believe that this line would fail should ret be made a const reference:
https://github.com/bitcoin/bitcoin/blob/0b90954416767f6213094f1f282e708c3e8a710b/src/test/descriptor_tests.cpp#L50
I guess I could change the signature to what you suggest and then assign ret to a local variable. Then there would still be a single copy operation so it's not less efficient or anything, but that would introduce an extra line of code. Do you think I should do it?
I agree that ret is not an indicative name. I think desc is better?
src/test/descriptor_tests.cpp
Outdated
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.
IMO it would be fine to declare in src/script/descriptor.h
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.
Done in commit 07c079a2fb49dca58a1d7e5e185a1cbc430381df.
src/test/descriptor_tests.cpp
Outdated
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.
nit, if (it == std::string::npos) break;
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.
Done in commit 07c079a2fb49dca58a1d7e5e185a1cbc430381df.
src/test/descriptor_tests.cpp
Outdated
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.
Add a comment like /* replace_apostrophe_with_h = */ true instead.
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.
Done in commit 07c079a.
src/test/descriptor_tests.cpp
Outdated
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.
Use std::string::find(char, ...) like find('\'').
nit, { here.
nit, remove unnecessary ( ).
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.
Done in commit 07c079a2fb49dca58a1d7e5e185a1cbc430381df. Also, applied the same change (find("'")->find('\'')) here:https://github.com/bitcoin/bitcoin/blob/07c079a2fb49dca58a1d7e5e185a1cbc430381df/src/test/descriptor_tests.cpp#L47
src/test/descriptor_tests.cpp
Outdated
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.
nit } else {
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.
Done in commit 07c079a2fb49dca58a1d7e5e185a1cbc430381df.
src/test/descriptor_tests.cpp
Outdated
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.
nit, { here
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.
Done in commit 07c079a2fb49dca58a1d7e5e185a1cbc430381df.
Thanks! Done in commit 07c079a. |
07c079a to
0b90954
Compare
|
Squashed commits. |
|
I believe this will be a lot simpler change now if you use Concept ACK anyhow |
0b90954 to
762dff3
Compare
762dff3 to
d0e54ff
Compare
Great! Now that |
|
@davereikher Travis tests can occasionally fail sporadically. I've rebooted the two failing instances for you. |
src/test/descriptor_tests.cpp
Outdated
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.
nit: indentation
src/test/descriptor_tests.cpp
Outdated
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.
There is actually a test case which used to be covered which is no longer ever covered - the use of h in the pub and ' in the prv and vice versa (because there was a 50% chance of each being changed independently)
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.
Thanks for your review. You are right, I completely missed this. I added code to cover all four test cases (no replacement, prv only, pub only and prv+pub) in b9ee63c.
d0e54ff to
35b07e9
Compare
Changed MaybeUseHInsteadOfApostrophy to UseHInsteadOfApostrophe. This function now always replaces apostrophes with 'h'. The original Check function was renamed to DoCheck and it's called with an additional parameter which tells it to either leave the prv and pub arguments as is or replace the apostrophes with 'h'. The test runs with apostrophes replaced in prv only, pub only, prv and pub and without replacement at all. Replacement of apostrophes in a descriptor and then running DoCheck is conditional on whether apostrophes are found in that descriptor. Additionally, instead of dropping the checksum recalculate it after replacing apostrophes with 'h' in the function UseHInsteadOfApostrophe using the GetDescriptorChecksum function. That way, this also introduces an indirect unit test to GetDescriptoChecksum.
35b07e9 to
b9ee63c
Compare
@fanquake Travis failed again, could you please reboot the failing instance here? |
meshcollider
left a comment
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.
utACK
achow101
left a comment
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.
Code Review ACK b9ee63c
|
@davereikher Thanks for sticking with this, nice first time contribution. |
b9ee63c Make descriptor test deterministic (David Reikher) Pull request description: This is an improvement to a test, inspired by #14343 - removing non determinism from a test. The test `descriptor_test` is non-deterministic, as it relies on the `MaybeUseHInsteadOfApostrophy` function which randomly either swaps all apostrophes with 'h' or doesn't at all in a descriptor. This fix makes both cases always run, if an apostrophe is found in a test descriptor. This does not reduce test coverage but removes the non-determinism. Additionally, the `MaybeUseHInsteadOfApostrophy` function removed the checksum if found at the end of a descriptor when the apostrophes are swapped by 'h's, since after being swapped the checksum is no longer correct. I instead added re-calculation of the checksum using the `DescriptorChecksum` function, which adds coverage for the case of a descriptors having 'h's instead of apostrophes and a checksum. This was previously lacking. To achieve this I had to move `DescriptorChecksum` and `PolyMod` out of the anonymous namespace in descriptor.cpp to make `DescriptorChecksum` accessible in descriptor_tests.cpp. All tests complete successfully (functional as well as unit tests). ACKs for top commit: achow101: Code Review ACK b9ee63c Tree-SHA512: 992c73a6644a07bfe7c72301ee2666f3c4845a012aaedd7a099a05cea8bdac84fa8280b28e44a7856260c00c0be1a6f1b6768f5694c2a22edf4c489e53fec424
|
Thanks a lot! I learned so much from just this small contribution and your reviews. Looking forward for more! :) |
|
@davereikher Very nice to have you onboard and thanks for this first contribution! Hope to see more contributions from you. If you want to take on non-determinism in tests you might want to take a look at The following unit tests are known to have non-deterministic line coverage: bitcoin/contrib/devtools/test_deterministic_coverage.sh Lines 16 to 36 in 3ca514d
Getting them deterministic in terms of line coverage would be great! More specifically that would allow us to reason about how a proposed change alters unit tests line coverage. Currently line coverage is a stochastic process :-) |
Thanks! Sounds great, I'll look into the non-determinism of those tests and I guess I will start with the low hanging fruit first :) |
Summary: ``` Changed MaybeUseHInsteadOfApostrophy to UseHInsteadOfApostrophe. This function now always replaces apostrophes with 'h'. The original Check function was renamed to DoCheck and it's called with an additional parameter which tells it to either leave the prv and pub arguments as is or replace the apostrophes with 'h'. The test runs with apostrophes replaced in prv only, pub only, prv and pub and without replacement at all. Replacement of apostrophes in a descriptor and then running DoCheck is conditional on whether apostrophes are found in that descriptor. Additionally, instead of dropping the checksum recalculate it after replacing apostrophes with 'h' in the function UseHInsteadOfApostrophe using the GetDescriptorChecksum function. That way, this also introduces an indirect unit test to GetDescriptoChecksum. ``` Backport of core [[bitcoin/bitcoin#16570 | PR16570]]. Test Plan: ninja check Reviewers: #bitcoin_abc, deadalnix Reviewed By: #bitcoin_abc, deadalnix Differential Revision: https://reviews.bitcoinabc.org/D7208
This is an improvement to a test, inspired by #14343 - removing non determinism from a test.
The test
descriptor_testis non-deterministic, as it relies on theMaybeUseHInsteadOfApostrophyfunction which randomly either swaps all apostrophes with 'h' or doesn't at all in a descriptor. This fix makes both cases always run, if an apostrophe is found in a test descriptor.This does not reduce test coverage but removes the non-determinism.
Additionally, the
MaybeUseHInsteadOfApostrophyfunction removed the checksum if found at the end of a descriptor when the apostrophes are swapped by 'h's, since after being swapped the checksum is no longer correct. I instead added re-calculation of the checksum using theDescriptorChecksumfunction, which adds coverage for the case of a descriptors having 'h's instead of apostrophes and a checksum. This was previously lacking.To achieve this I had to move
DescriptorChecksumandPolyModout of the anonymous namespace in descriptor.cpp to makeDescriptorChecksumaccessible in descriptor_tests.cpp.All tests complete successfully (functional as well as unit tests).