-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
[Bug] Fix root cause that caused StatsGranularTabsTest to fail. #20606
Conversation
📲 You can test the changes from this Pull Request in WordPress by scanning the QR code below to install the corresponding build.
|
📲 You can test the changes from this Pull Request in Jetpack by scanning the QR code below to install the corresponding build.
|
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## trunk #20606 +/- ##
==========================================
- Coverage 40.50% 40.48% -0.03%
==========================================
Files 1474 1474
Lines 67961 67979 +18
Branches 11235 11246 +11
==========================================
- Hits 27529 27519 -10
- Misses 37949 37966 +17
- Partials 2483 2494 +11 ☔ View full report in Codecov by Sentry. |
@@ -94,7 +94,7 @@ abstract class BaseStatsUseCase<DOMAIN_MODEL, UI_STATE>( | |||
if (!state.cached) { | |||
val updatedCachedData = loadCachedData() | |||
if (domainModel != updatedCachedData) { | |||
domainModel = updatedCachedData | |||
domainModel = state.model |
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.
Does it mean the whole if{} block is redundant 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.
At first glance, the answer is that its not redundant. Not because of my change, but because of the updateState()
. If the state values are the same, I imagine we don't want to fire a call to updateState
. But this is where your input will matter as there may be details about fluxc I don't fully understand. Presumably, if we are using a data class, which I believe we do for those models, data from the cache can equal data from the web. Basing this solely off the docs. I could be wrong. Your thoughts 😄 ?
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.
FluxC is also long before my time!
Wdyt? @irfano
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 know what. I also just realized that I see why you are saying that. domainModel != updatedCachedData
is confusing. It should be domainModel != state
. Does that make more sense @ravishanker ?
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 know what. I also just realized that I see why you are saying that. domainModel != updatedCachedData is confusing. It should be domainModel != state. Does that make more sense @ravishanker ?
Yes! We can remove val updatedCachedData = loadCachedData()
line and check domainModel != state
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.
When a user first loads stats, there is no cache. So it must fetch from the network. When it does that, the domainModel is set to the cache, which technically doesn't exist yet.
This is not entirely accurate. FluxC writes to the cache before sending responses to the WordPress-Android. So, before val updatedCachedData = loadCachedData()
line, it is already fetched and the cache has existed. But I do agree with your final suggestion.
It's not entirely clear to me why this is being done. In evaluateState if we got state back, presumably we should use that as it would be the most up-to-date. So I just tried it.
I agree!
I tested trunk
and this PR multiple times and have never encountered the issue on this PR while I could reproduce it on trunk
. Additionally, I compared the content of all the cards between PR and the trunk
version, and they were identical.
Andy, you can address Ravi's comment, and then I believe we're good to merge. 🟢
@@ -94,7 +94,7 @@ abstract class BaseStatsUseCase<DOMAIN_MODEL, UI_STATE>( | |||
if (!state.cached) { | |||
val updatedCachedData = loadCachedData() | |||
if (domainModel != updatedCachedData) { | |||
domainModel = updatedCachedData | |||
domainModel = state.model |
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 know what. I also just realized that I see why you are saying that. domainModel != updatedCachedData is confusing. It should be domainModel != state. Does that make more sense @ravishanker ?
Yes! We can remove val updatedCachedData = loadCachedData()
line and check domainModel != state
Thanks for adding the missing context @irfano . Very useful information. And actually makes me wonder why we don't just listen to the database cache updates directly and not deal with the network at all 😅 . But that's out of scope for this PR. I went ahead and made that change. Ended up removing the |
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 work 🚀
Hey @irfano when you get a chance, no rush. Quick re-review. I made the changes we talked about. But had to update a bunch of unit tests. I may have went down a rabbit hole. But the error wasn't clear here, so I went the extra mile and updated the unit tests so they pass locally without the need to change the flavor, which is more proper anyways. Note: Most of the files edited were test files. Small exceptions. |
...src/test/java/org/wordpress/android/ui/stats/refresh/lists/detail/PostDayViewsUseCaseTest.kt
Outdated
Show resolved
Hide resolved
.../src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/BaseStatsUseCaseTest.kt
Show resolved
Hide resolved
.../src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/BaseStatsUseCaseTest.kt
Outdated
Show resolved
Hide resolved
.../src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/BaseStatsUseCaseTest.kt
Show resolved
Hide resolved
...g/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/OverviewUseCaseTest.kt
Show resolved
Hide resolved
.../wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/ReferrersUseCaseTest.kt
Show resolved
Hide resolved
...rdpress/android/ui/stats/refresh/lists/sections/insights/usecases/AllTimeStatsUseCaseTest.kt
Show resolved
Hide resolved
Ready for re-review @irfano . Let me know if you have any additional questions. All good stuff as we should questions the changes 🙏🏻 |
Thank you for bearing my comments, @notandyvee. 😅 I'm curious about your opinion on my two open comments. Then we're good to merge. |
Quality Gate passedIssues Measures |
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! 👍🏻 Thank you for your contributions to improve our tests, Andy. Great work! 🚀
Fixes #18065
There was a failing UI test that appeared flaky,
StatsGranularTabsTest
. Retrying appears to fix it. Every once in a while I also notice stats cards don't load properly. It's super hard to reproduce, even in UI tests.At first I figured out part of the issue. I noticed each failing stats card would have an empty
BlockListItem
list. Each card can have multipleBlockListItem
s. When theStatsBlock
has noBlockListItem
, it can't do anything, so presents itself as a card with nothing in it. This is unfortunate, but not really an issue. The real issue is why aStatsBlock
can even have an empty list.After further testing I learned that what leads to this issue is actually a race condition. The UI is dependent on setting the
domainModel
inBaseStatsUseCase
. In most cases, the domain model is present quickly when we load the UI. But during UI tests situations arise where thedomainModel
takes a bit longer to load, leading to a race condition. As far as I can tell, it really only happens (or noticeable) when you first login or switch to a blog for the first time. The change I made inBaseStatsUseCase
best illustrates why I think this fixes the issue.When a user first loads stats, there is no cache. So it must fetch from the network. When it does that, the
domainModel
is set to the cache, which technically doesn't exist yet. This happens inevaluateState
when a network call is made.It's not entirely clear to me why this is being done. In
evaluateState
if we got state back, presumably we should use that as it would be the most up-to-date. So I just tried it.I re-ran the previously failing UI test dozens of times. Couldn't reproduce it. I also 🚬 tested stats and don't notice any differences.
To Test:
StatsGranularTabTest
must pass 100% of the time. If on first pass it fails, this PR is pointless 😅 .Regression Notes
Potential unintended areas of impact
Stats cards loading.
What I did to test those areas of impact (or what existing automated tests I relied on)
Manual test and UI test.
What automated tests I added (or what prevented me from doing so)
n/a
PR Submission Checklist:
RELEASE-NOTES.txt
if necessary.