Status Update
Comments
kl...@google.com <kl...@google.com> #2
There's a little more context in
ap...@google.com <ap...@google.com> #3
Branch: androidx-main
commit 013365e5bd19899250054dabe311f3bc9d8837e9
Author: Zach Klippenstein <klippenstein@google.com>
Date: Thu Oct 20 10:32:31 2022
Add some tests for TestMonotonicFrameClock's current behavior.
This is in preparation for modifying the clock to include a custom
continuation interceptor.
Bug:
Test: TestMonotonicFrameClockTest
Change-Id: I771b25673c96b41f8501600677dfcbe3ff9fb6fc
M compose/ui/ui-test/build.gradle
A compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/PhaseOrderingTest.kt
M compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/TestMonotonicFrameClockTest.kt
A compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/util/TestCounter.kt
ap...@google.com <ap...@google.com> #4
Branch: androidx-main
commit f61c557f4526dfa0e33d070de9c262e788184e8a
Author: Zach Klippenstein <klippenstein@google.com>
Date: Thu Oct 20 10:33:11 2022
Defer dispatching coroutines resumed during animation callbacks in UI tests.
This is a workaround for the fact that we use an unconfined dispatcher
in UI tests (
layout passes for frames in some cases (
TestMonotonicFrameClock gets a continuation interceptor which wraps continuations with
behavior that will usually no-op, but if the continuation is resumed
while frame callbacks are running, it will defer resuming the
underlying continuation until all the frame callbacks have finished.
This matches how continuations are dispatched in non-test code, since
that code does not use an unconfined dispatcher.
In order to preserve test delay skipping, the interceptor implements
Delay and delegates it to the underlying dispatcher, just like
ApplyingContinuationInterceptor is already doing.
This CL also adds a callback that runs after each frame in
TestMonotonicFrameClock. This change was originally in aosp/2121435.
This allows us to perform measure and layout after each frame if
necessary, which aligns the measure/layout timing with production.
Bug:
Bug:
Bug:
Test: PhaseOrderingTest, TestMonotonicFrameClockTest
Relnote: "In UI tests using a Compose rule, continuations resumed during
`withFrameNanos` callbacks will not be dispatched until after all
frame callbacks have finished running. This matches the behavior of
compose when running normally. However, tests that rely on the old
behavior may fail. This should only affect code that calls
`withFrameNanos` or `withFrameMillis` directly, and has logic
outside of callback passed to those functions that may need to be
moved inside the callbacks. See the animation test changes in this
CL for examples."
Relnote: "Added optional `onPerformTraversals: (Long) -> Unit` parameter to
`TestMonotonicFrameClock` constructor and factory function to run
code after `withFrameNanos` callbacks but before resuming callers'
coroutines."
Change-Id: Idb41309445a030c91e8e4ae05daa9642b450505c
M compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/SingleValueAnimationTest.kt
M compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/AnimatedContentTest.kt
M compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/CrossfadeTest.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableFocusableInteractionTest.kt
M compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunner.android.kt
M compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/ComposeUiTest.android.kt
M compose/ui/ui-test-junit4/src/commonMain/kotlin/androidx/compose/ui/test/ApplyingContinuationInterceptor.kt
M compose/ui/ui-test/api/public_plus_experimental_current.txt
M compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/PhaseOrderingTest.kt
M compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/TestMonotonicFrameClockTest.kt
A compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/internal/DelayPropagatingContinuationInterceptorWrapper.kt
A compose/ui/ui-test/src/jvmMain/kotlin/androidx/compose/ui/test/FrameDeferringContinuationInterceptor.kt
M compose/ui/ui-test/src/jvmMain/kotlin/androidx/compose/ui/test/TestMonotonicFrameClock.jvm.kt
ap...@google.com <ap...@google.com> #5
Branch: androidx-main
commit 803a2a7da2b86cf4ac9930a897ec62d9a73b1fda
Author: stevebower <stevebower@google.com>
Date: Thu Dec 01 13:37:43 2022
Remove withFrameNanos workaround now that
Bug: 259134313
Test: ./gradlew :wear:compose:compose-material:connectedCheck --info --daemon
Change-Id: I74a9f3fd67935f468411af75d7d7cf33267f9b5a
M wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ScalingLazyColumn.kt
ba...@gmail.com <ba...@gmail.com> #6
Is there a workaround? Currently we use the following.
val scope = rememberCoroutineScope {
// ui test broke without this.
Dispatchers.Main.immediate
}
Otherwise some navigateToXXX
callbacks are invoked on a Room dispatcher. Which breaks the NavGraph.
da...@google.com <da...@google.com> #7
My tests are not doing their job of catching bugs due to this issue.
It's pretty bad for the test environment to have different behavior to production. It leads to bugs and makes it so that we can't verify behavior in tests properly.
je...@google.com <je...@google.com>
lp...@google.com <lp...@google.com> #8
I ran into this in
We actually have existing tests that should catch this category of issue, essentially the problem looks like:
scope.launch { }
...some other method calls
scope.cancel()
Normally the cancel will happen before the launch, so the block will never start executing, but in tests because it is unconfined this was accidentally passing as the launch always started first. Manually adding a yield() inside the launch makes the test start to fail, although that is still slightly different as normally we wouldn't even get to the point where the launch {} takes effect and invokes the block.
is...@gmail.com <is...@gmail.com> #9
I see this issue has had some recent movement with a pending PR for adding the ability to set the dispatcher responsible for coroutines related to composition to the StandardTestDispatcher. I have ported these changes over to a project I'm working on and have seen a significant increase in the stability of my team's end-to-end tests that leverage the Compose Testing APIs. Previously, we were encountering issues that were only "solved" by passing in Dispatchers.Main
in our app code for the majority of our utilizations of rememberCoroutineScope
and LaunchedEffect
. Are the changes in the PR associated with this issue being prioritized, and when can we expect them to move through?
Description
Currently Compose UI tests use an unconfined coroutine dispatcher for all coroutines that are part of the composition (e.g.
LaunchedEffect
,rememberComposeScope
, and even the coroutine running theRecomposer
itself).This is inconsistent with how coroutines are dispatched in non-test environments, where we use the main immediate dispatcher. It is also dangerous because unconfined dispatchers can change the order that code will run in sneaky ways.