Overview
testKeyboardAnimationWillWaitUIThreadVsync in FlutterViewControllerTest.mm is flaky. It attempts to verify that the keyboard animation vsync callback is synchronised with the UI thread by blocking that thread with sleep(1) and then asserting that at least ~1 second elapsed before the callback fired. Because the assertion depends on thread scheduling rather than on an observable property of the code, it intermittently fails.
The intent of the test is: post a long-running (sleep(1)) task to the UI thread, trigger a vsync, and assert the keyboard callback does not complete until the UI-thread work has finished — i.e. fulfillTime - startTime >= ~1s.
The problem is that the pass/fail condition is a wall-clock timing assertion coupled to thread scheduling:
- The keyboard callback is not delivered on the UI task runner. It's delivered via
dispatch_async(dispatch_get_main_queue()) (see [FlutterKeyboardInsetManager -setUpKeyboardAnimationVsyncClient:]). The sleep(1) runs on the UI thread, while the callback that records fulfillTime runs on the platform (main) thread.
- The test relies on a specific interleaving between the sleep task, the display-link registration/trigger, and the main-queue hop to hold
fulfillTime back by a full second. That interleaving isn't guaranteed. Under different scheduling — loaded CI machines, thread-pool timing, run-loop servicing order — the measured fulfillTime can end up short of delayTime - epsilon and the assertion fails.
- Blocking a thread with
sleep and asserting on elapsed time is inherently timing-dependent, so the test is sort of racy by design.
I've run into this a fair bit in tweaks to the keyboard manager code, under repeated runs.
We should rewrite the test to assert the contract the code actually implements (asynchronous delivery of the callback) rather than the scheduling-dependent timing, removing the sleep and the wall-clock assertion.
Overview
testKeyboardAnimationWillWaitUIThreadVsyncin FlutterViewControllerTest.mm is flaky. It attempts to verify that the keyboard animation vsync callback is synchronised with the UI thread by blocking that thread withsleep(1)and then asserting that at least ~1 second elapsed before the callback fired. Because the assertion depends on thread scheduling rather than on an observable property of the code, it intermittently fails.The intent of the test is: post a long-running (
sleep(1)) task to the UI thread, trigger a vsync, and assert the keyboard callback does not complete until the UI-thread work has finished — i.e.fulfillTime - startTime >= ~1s.The problem is that the pass/fail condition is a wall-clock timing assertion coupled to thread scheduling:
dispatch_async(dispatch_get_main_queue())(see[FlutterKeyboardInsetManager -setUpKeyboardAnimationVsyncClient:]). Thesleep(1)runs on the UI thread, while the callback that recordsfulfillTimeruns on the platform (main) thread.fulfillTimeback by a full second. That interleaving isn't guaranteed. Under different scheduling — loaded CI machines, thread-pool timing, run-loop servicing order — the measuredfulfillTimecan end up short ofdelayTime - epsilonand the assertion fails.sleepand asserting on elapsed time is inherently timing-dependent, so the test is sort of racy by design.I've run into this a fair bit in tweaks to the keyboard manager code, under repeated runs.
We should rewrite the test to assert the contract the code actually implements (asynchronous delivery of the callback) rather than the scheduling-dependent timing, removing the sleep and the wall-clock assertion.