Skip to content
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

Flaky test fields iteration order #4095

Merged

Conversation

yyfMichaelYan
Copy link
Contributor

Fix two flaky tests, testAssembleLongPollRefreshUrl and testAssembleLongPollRefreshUrlWithMultipleNamespaces.

This flaky test is found by using Nondex, a tool used to find flaky tests in Java projects. The assertion on Line#511 of testAssembleLongPollRefreshUrl and Line#538 of testAssembleLongPollRefreshUrlWithMultipleNamespaces may fail because the iteration order of the two fields, namespaceName and notificationId, of the class ApolloConfigNotification is non-deterministic in the method of GSON.toJson. As a result, the resulting string may have notificationId before or after namespaceName. Since the GSON package is not maintained in this repo, it's impossible to change the iteration order of the fields. Therefore, to fix the flaky tests, all possible iteration orders of the two fields are covered in the assertions.

@codecov-commenter
Copy link

codecov-commenter commented Nov 12, 2021

Codecov Report

Merging #4095 (21e1312) into master (9e19898) will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff            @@
##             master    #4095   +/-   ##
=========================================
  Coverage     52.49%   52.49%           
+ Complexity     2614     2613    -1     
=========================================
  Files           484      484           
  Lines         15206    15206           
  Branches       1572     1572           
=========================================
  Hits           7982     7982           
  Misses         6671     6671           
  Partials        553      553           
Impacted Files Coverage Δ
...o/openapi/filter/ConsumerAuthenticationFilter.java 94.11% <0.00%> (-5.89%) ⬇️
...ervice/service/ReleaseMessageServiceWithCache.java 85.88% <0.00%> (-1.18%) ⬇️
.../apollo/internals/RemoteConfigLongPollService.java 78.31% <0.00%> (+1.20%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9e19898...21e1312. Read the comment docs.

@@ -510,7 +510,10 @@ public void testAssembleLongPollRefreshUrl() throws Exception {
assertTrue(longPollRefreshUrl.contains("cluster=someCluster%2B+%26.-_someSign"));
assertTrue(longPollRefreshUrl.contains(
"notifications=%5B%7B%22namespaceName%22%3A%22" + someNamespace
+ "%22%2C%22notificationId%22%3A" + 1 + "%7D%5D"));
+ "%22%2C%22notificationId%22%3A" + 1 + "%7D%5D")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will it make things easy if we make the notificationsMap treemap?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will not work by changing the implementation of notificationsMap to TreeMap. The iteration order of ImmutableMap is deterministic, which is why in the assertion of testAssembleLongPollRefreshUrlWithMultipleNamespaces, someName always comes before anotherName. In other words, these two flaky tests are not caused by the iteration order of entries of the map. However, for either someName and anotherName, the order of the two fields, namespace and notificationId, is non-deterministic. This is why all 4 possible combinations are covered in the assertTrue in testAssembleLongPollRefreshUrlWithMultipleNamespaces.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got your point, it's not the map but the gson.toJson that might make the fields in different order.
I checked the com.google.gson.internal.bind.ReflectiveTypeAdapterFactory#getBoundFields method, and it seems the order is determined by the java.lang.Class#getDeclaredFields.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your information! Yes. I also went through all implementations of the write method which is hidden in the toJson method. I agree that the order may be determined by java.lang.Class#getDeclaredFields. However, since it's built in some package not maintained by this repository, I feel that it's better to change the assertion in our tests rather than modify the code of java.lang.Class#getDeclaredFields?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with you that the order might change in different execution environments. But the current changes to test all combinations of the URLs look quite complicated, which is not easy to maintain, e.g. if someone needs to add more namespaces to testAssembleLongPollRefreshUrlWithMultipleNamespaces.
Is there some way to make the assertions simpler while still could works when the order of fields is changed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I just updated both assertions. The assertions now will check that every field is converted to string, but it doesn't check the order of the fields. The assertions are now less strict than before, because some characters between neighboring fields are not checked, such as {, }, and ,, but they look very simple and I think it's okay if we don't check those characters.

+ "%22%2C%22notificationId%22%3A" + someNotificationId
+ "%7D%2C%7B%22namespaceName%22%3A%22" + anotherNamespace
+ "%22%2C%22notificationId%22%3A" + anotherNotificationId + "%7D%5D"));
longPollRefreshUrl.contains("notifications=%5B%7B" + someNamespaceEntry + "%2C" + someNotificationIdEntry
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will it make things easy if we make the notificationsMap treemap?

Copy link
Member

@nobodyiam nobodyiam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@nobodyiam nobodyiam merged commit 80a33ab into apolloconfig:master Nov 26, 2021
@github-actions github-actions bot locked and limited conversation to collaborators Nov 26, 2021
@nobodyiam nobodyiam added this to the 2.0.0 milestone Jan 1, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants