Repository navigation
[devicelab] Fix memory benchmark tap synchronization and swipe flakes - #194457
Conversation
Replaces the unawaited `Future.any` tap loop in `MemoryTest.tapNotification()` with sequential tap-and-wait retries so in-flight `adb shell input tap` commands do not leak into the benchmark after `TAPPED` is received, and guards `Completer.complete()` against duplicate `TAPPED` logcat markers. Updates `fast_scroll_heavy_gridview__memory` and `fast_scroll_large_images__memory` to drive scrolling deterministically in-process with `LiveWidgetController.fling` and `endOfAnimation()` instead of blind `adb shell input swipe` commands and fixed sleeps, and adds bounded 60-second timeouts to `MemoryTest` and `ReportedDurationTest` logcat waits. Fixes flutter#149901
There was a problem hiding this comment.
Code Review
This pull request refactors memory benchmark tests to perform automated flinging internally within the test apps rather than using external ADB swipe commands, and improves tap synchronization with sequential retries and timeouts. Feedback on these changes suggests simplifying the widget tree in the benchmark apps by removing redundant GestureDetector and IgnorePointer wrappers, and replacing null-aware operators (?.) with null-assertions (!) on receivedNextMessage to prevent silent test bypasses and ensure fast failures.
| runApp( | ||
| GestureDetector( | ||
| child: const IgnorePointer( | ||
| ignoring: false, | ||
| child: MacrobenchmarksApp(initialRoute: kHeavyGridViewRouteName), | ||
| ), | ||
| ), | ||
| ); |
There was a problem hiding this comment.
Since runApp completely replaces the widget tree, wrapping the app in a redundant GestureDetector (with no callbacks) and IgnorePointer(ignoring: false) is unnecessary. We can simplify this by directly calling runApp with the MacrobenchmarksApp widget.
| runApp( | |
| GestureDetector( | |
| child: const IgnorePointer( | |
| ignoring: false, | |
| child: MacrobenchmarksApp(initialRoute: kHeavyGridViewRouteName), | |
| ), | |
| ), | |
| ); | |
| runApp(const MacrobenchmarksApp(initialRoute: kHeavyGridViewRouteName)); |
References
- Suggest simplification and refactoring: Assess whether the code can be made simpler or refactored to enhance readability and maintainability. (link)
| runApp( | ||
| GestureDetector( | ||
| child: const IgnorePointer( | ||
| ignoring: false, | ||
| child: MacrobenchmarksApp(initialRoute: kLargeImagesRouteName), | ||
| ), | ||
| ), | ||
| ); |
There was a problem hiding this comment.
Since runApp completely replaces the widget tree, wrapping the app in a redundant GestureDetector (with no callbacks) and IgnorePointer(ignoring: false) is unnecessary. We can simplify this by directly calling runApp with the MacrobenchmarksApp widget.
| runApp( | |
| GestureDetector( | |
| child: const IgnorePointer( | |
| ignoring: false, | |
| child: MacrobenchmarksApp(initialRoute: kLargeImagesRouteName), | |
| ), | |
| ), | |
| ); | |
| runApp(const MacrobenchmarksApp(initialRoute: kLargeImagesRouteName)); |
References
- Suggest simplification and refactoring: Assess whether the code can be made simpler or refactored to enhance readability and maintainability. (link)
| prepareForNextMessage('AppLifecycleState.paused'); | ||
| await device!.shellExec('input', <String>['keyevent', 'KEYCODE_BACK']); | ||
| await receivedNextMessage; | ||
| await receivedNextMessage?.timeout(MemoryTest.nextMessageTimeout); |
There was a problem hiding this comment.
Using the null-aware operator ?. on receivedNextMessage means that if receivedNextMessage is null (e.g., due to a developer error where prepareForNextMessage was not called), the expression will silently evaluate to null and await null will complete immediately without waiting. Using the null-assertion operator ! instead ensures that the test fails fast with a clear error if receivedNextMessage is null, preventing silent test bypasses.
| await receivedNextMessage?.timeout(MemoryTest.nextMessageTimeout); | |
| await receivedNextMessage!.timeout(MemoryTest.nextMessageTimeout); |
| fail('unable to launch activity'); | ||
| } | ||
| await receivedNextMessage; | ||
| await receivedNextMessage?.timeout(MemoryTest.nextMessageTimeout); |
There was a problem hiding this comment.
Using the null-aware operator ?. on receivedNextMessage means that if receivedNextMessage is null (e.g., due to a developer error where prepareForNextMessage was not called), the expression will silently evaluate to null and await null will complete immediately without waiting. Using the null-assertion operator ! instead ensures that the test fails fast with a clear error if receivedNextMessage is null, preventing silent test bypasses.
| await receivedNextMessage?.timeout(MemoryTest.nextMessageTimeout); | |
| await receivedNextMessage!.timeout(MemoryTest.nextMessageTimeout); |
| print('awaiting "ready" message...'); | ||
| await receivedNextMessage; | ||
| print('awaiting "ready" message... (timeout: $nextMessageTimeout)'); | ||
| await receivedNextMessage?.timeout(nextMessageTimeout); |
There was a problem hiding this comment.
Using the null-aware operator ?. on receivedNextMessage means that if receivedNextMessage is null (e.g., due to a developer error where prepareForNextMessage was not called), the expression will silently evaluate to null and await null will complete immediately without waiting. Using the null-assertion operator ! instead ensures that the test fails fast with a clear error if receivedNextMessage is null, preventing silent test bypasses.
| await receivedNextMessage?.timeout(nextMessageTimeout); | |
| await receivedNextMessage!.timeout(nextMessageTimeout); |
| await device!.tap(_tapCoordinate, _tapCoordinate); | ||
| print('awaiting "tapped" message... (timeout: $_tapRetryTimeout)'); | ||
| try { | ||
| await receivedNextMessage?.timeout(_tapRetryTimeout); |
There was a problem hiding this comment.
Using the null-aware operator ?. on receivedNextMessage means that if receivedNextMessage is null (e.g., due to a developer error where prepareForNextMessage was not called), the expression will silently evaluate to null and await null will complete immediately without waiting. Using the null-assertion operator ! instead ensures that the test fails fast with a clear error if receivedNextMessage is null, preventing silent test bypasses.
| await receivedNextMessage?.timeout(_tapRetryTimeout); | |
| await receivedNextMessage!.timeout(_tapRetryTimeout); |
| print('awaiting "done" message...'); | ||
| await receivedNextMessage; | ||
| print('awaiting "done" message... (timeout: $nextMessageTimeout)'); | ||
| await receivedNextMessage?.timeout(nextMessageTimeout); |
There was a problem hiding this comment.
Using the null-aware operator ?. on receivedNextMessage means that if receivedNextMessage is null (e.g., due to a developer error where prepareForNextMessage was not called), the expression will silently evaluate to null and await null will complete immediately without waiting. Using the null-assertion operator ! instead ensures that the test fails fast with a clear error if receivedNextMessage is null, preventing silent test bypasses.
| await receivedNextMessage?.timeout(nextMessageTimeout); | |
| await receivedNextMessage!.timeout(nextMessageTimeout); |
flutter/flutter@fffd8c2...b98afb5 2026-11-11 [email protected] Roll Dart SDK from 172cf8f121c0 to 8f15c4ca2d41 (1 revision) (flutter/flutter#196367) 2026-11-11 [email protected] Roll Skia from 657283e78115 to cc4c54f752ba (5 revisions) (flutter/flutter#196365) 2026-11-11 [email protected] Roll Skia from 2605c4a9761c to 657283e78115 (1 revision) (flutter/flutter#196358) 2026-11-11 [email protected] Roll Dart SDK from 890c6d15334c to 172cf8f121c0 (1 revision) (flutter/flutter#196352) 2026-11-11 [email protected] [flutter_tools] Migrate general.shard project, runner, and web tests off globals (flutter/flutter#196128) 2026-11-11 [email protected] [flutter_tools] Migrate test helpers and integration tests off globals (flutter/flutter#196124) 2026-11-11 [email protected] [web] Report skwasm WebGL context creation failure instead of hanging (flutter/flutter#193293) 2026-11-11 [email protected] [flutter_tools] Migrate commands.shard tests off globals (flutter/flutter#196139) 2026-11-11 [email protected] [devicelab] Fix memory benchmark tap synchronization and swipe flakes (flutter/flutter#194457) 2026-11-11 [email protected] Roll Skia from 06062e36d938 to 2605c4a9761c (2 revisions) (flutter/flutter#196350) 2026-11-11 [email protected] Do not wait again for a closed mock event stream in tear down (flutter/flutter#196110) 2026-11-11 [email protected] [gen_l10n] Add `use-explicit-plural-values` option (flutter/flutter#194816) 2026-11-11 [email protected] Roll Skia from 7bd33f8a3b70 to 06062e36d938 (2 revisions) (flutter/flutter#196340) 2026-11-11 [email protected] Skip redundant work in plugin_test_* (flutter/flutter#196258) 2026-11-10 [email protected] Drop test_timeout_secs from shards that no longer need it (flutter/flutter#196277) 2026-11-10 [email protected] Roll Dart SDK from 15c1699d3eeb to 890c6d15334c (1 revision) (flutter/flutter#196321) 2026-11-10 [email protected] iOS: Extract FlutterPlatformViewsController platform-thread state to Obj-C (flutter/flutter#196142) 2026-11-10 [email protected] Remove `Mac web_tool_tests` and take new subshard builders out of bringup (flutter/flutter#196276) 2026-11-10 [email protected] Roll Skia from 9c3d43d8a04b to 7bd33f8a3b70 (12 revisions) (flutter/flutter#196317) 2026-11-10 [email protected] Take customer_testing_devtools out of bringup (flutter/flutter#196239) 2026-11-10 [email protected] Roll ICU from a0ea93436c52 to b09986242cfb (1 revision) (flutter/flutter#196316) 2026-11-10 [email protected] [flutter_tools] Avoid unlinking native_assets.json and handle EXDEV (flutter/flutter#196134) 2026-11-10 [email protected] Remove old `Windows tool_integration_tests` builders and take the new shards out of bringup (flutter/flutter#196275) 2026-11-10 [email protected] [SwiftPM] Disable tests for when settings fail to compile (flutter/flutter#196310) 2026-11-10 [email protected] Roll Fuchsia Test Scripts from PXc5D7S_q6sIYpfFJ... to oEU515sDqSTvD8Spx... (flutter/flutter#196289) 2026-11-10 [email protected] Fix ime insets animation jump (flutter/flutter#191453) 2026-11-10 [email protected] Link to original PR in automatic cherry-pick PRs (flutter/flutter#196130) 2026-11-10 [email protected] Roll Packages from 565c657 to 08faf64 (9 revisions) (flutter/flutter#196296) 2026-11-10 [email protected] Roll Skia from b11315d0a096 to 9c3d43d8a04b (45 revisions) (flutter/flutter#196295) 2026-11-10 [email protected] Replace type-specific window widgets with a single Window widget (flutter/flutter#196002) 2026-11-10 [email protected] Roll Dart SDK from a2baae273bb2 to 15c1699d3eeb (2 revisions) (flutter/flutter#196294) If this roll has caused a breakage, revert this CL and set the roller to dry run mode using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC [email protected],[email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Fixes synchronization and timeout flakes in Devicelab memory benchmarks:
Future.anyrace inMemoryTest.tapNotification()(which firedadb shell input tap 100 100every 100ms and could leave an unawaited tap in flight afterTAPPEDwas received, causing stray taps or duplicateCompleter.complete()throws influtter_gallery__memory_navandcomplex_layout_scroll_perf__memory) with a sequential retry loop (up to 10 attempts, 1s timeout per tap).Completer.complete()inMemoryTest.handleLogcatLine(),ReportedDurationTest.run(),memory_nav.dart, andscroll_perf.dartagainst duplicate benchmark markers.fast_scroll_heavy_gridview__memoryandfast_scroll_large_images__memoryto drive scrolling deterministically in-process withLiveWidgetController(WidgetsBinding.instance).fling(...)+endOfAnimation()and==== MEMORY BENCHMARK ==== DONE ====via the standardMemoryTest.useMemory()flow instead of blindadb shell input swipecoordinates and fixed delays.MemoryTest.nextMessageTimeoutandReportedDurationTest.durationTimeout) toREADY,DONE,AppLifecycleState.paused, anddurationCompleter.futurelogcat waits so missed log lines fail fast instead of hanging the LUCI builder for 30 minutes.Fixes #149901
Pre-launch Checklist
///).