From e80bbd929e1823ad8224e1e031de53570adebff2 Mon Sep 17 00:00:00 2001 From: John McDole Date: Mon, 17 Jun 2024 10:14:47 -0700 Subject: [PATCH] Fix flaky complex_layout_scroll_perf__memory & flutter_gallery__memory_nav (#150368) Initial tap is missing sometimes; either its never delivered or it is delivered before gesture controller is hooked up. 1: Update MemoryTest to have option `requiresTapToStart` guarding the new paths 2: Update the two perf tests that appear to be flaky to output when TAPPED is received 3: Update the MemoryTest to keep tapping while waiting for TAPPED Tested on devicelab: * setting iterations=1 * removing the timeout before READY * running tests in a while loop Before this change, you could get the test to hang often. After this change you'll see "tapping device... [x]" where x is the counter. Fixes https://github.com/flutter/flutter/issues/150096 --- .../test_memory/scroll_perf.dart | 10 ++--- .../complex_layout_scroll_perf__memory.dart | 1 + .../tasks/flutter_gallery__memory_nav.dart | 1 + dev/devicelab/lib/tasks/perf_tests.dart | 40 +++++++++++++++++-- .../test_memory/memory_nav.dart | 6 ++- 5 files changed, 47 insertions(+), 11 deletions(-) diff --git a/dev/benchmarks/complex_layout/test_memory/scroll_perf.dart b/dev/benchmarks/complex_layout/test_memory/scroll_perf.dart index b06849c8a5..46e3f2daff 100644 --- a/dev/benchmarks/complex_layout/test_memory/scroll_perf.dart +++ b/dev/benchmarks/complex_layout/test_memory/scroll_perf.dart @@ -23,7 +23,7 @@ Future main() async { final Completer ready = Completer(); runApp(GestureDetector( onTap: () { - debugPrint('Received tap.'); + debugPrint('==== MEMORY BENCHMARK ==== TAPPED ===='); ready.complete(); }, behavior: HitTestBehavior.opaque, @@ -32,16 +32,14 @@ Future main() async { ), )); await SchedulerBinding.instance.endOfFrame; - - /// Wait 50ms to allow the raster thread to actually put up the frame. (The - /// endOfFrame future ends when we send the data to the engine, before - /// the raster thread has had a chance to rasterize, etc.) - await Future.delayed(const Duration(milliseconds: 50)); debugPrint('==== MEMORY BENCHMARK ==== READY ===='); await ready.future; // waits for tap sent by devicelab task debugPrint('Continuing...'); + // Wait out any errant taps due to synchronization + await Future.delayed(const Duration(milliseconds: 200)); + // remove onTap handler, enable pointer events for app runApp(GestureDetector( child: const IgnorePointer( diff --git a/dev/devicelab/bin/tasks/complex_layout_scroll_perf__memory.dart b/dev/devicelab/bin/tasks/complex_layout_scroll_perf__memory.dart index f3784a3b30..ae691b0e3d 100644 --- a/dev/devicelab/bin/tasks/complex_layout_scroll_perf__memory.dart +++ b/dev/devicelab/bin/tasks/complex_layout_scroll_perf__memory.dart @@ -13,5 +13,6 @@ Future main() async { '${flutterDirectory.path}/dev/benchmarks/complex_layout', 'test_memory/scroll_perf.dart', 'com.yourcompany.complexLayout', + requiresTapToStart: true, ).run); } diff --git a/dev/devicelab/bin/tasks/flutter_gallery__memory_nav.dart b/dev/devicelab/bin/tasks/flutter_gallery__memory_nav.dart index 3b83a78bb8..d056d64d33 100644 --- a/dev/devicelab/bin/tasks/flutter_gallery__memory_nav.dart +++ b/dev/devicelab/bin/tasks/flutter_gallery__memory_nav.dart @@ -11,5 +11,6 @@ Future main() async { '${flutterDirectory.path}/dev/integration_tests/flutter_gallery', 'test_memory/memory_nav.dart', 'io.flutter.demo.gallery', + requiresTapToStart: true, ).run); } diff --git a/dev/devicelab/lib/tasks/perf_tests.dart b/dev/devicelab/lib/tasks/perf_tests.dart index de9dfec8f1..8bb4b64652 100644 --- a/dev/devicelab/lib/tasks/perf_tests.dart +++ b/dev/devicelab/lib/tasks/perf_tests.dart @@ -1968,11 +1968,12 @@ class CompileTest { /// Measure application memory usage. class MemoryTest { - MemoryTest(this.project, this.test, this.package); + MemoryTest(this.project, this.test, this.package, {this.requiresTapToStart = false}); final String project; final String test; final String package; + final bool requiresTapToStart; /// Completes when the log line specified in the last call to /// [prepareForNextMessage] is seen by `adb logcat`. @@ -2061,6 +2062,38 @@ class MemoryTest { await receivedNextMessage; } + /// Taps the application and looks for acknowldgement. + /// + /// This is used by several tests to ensure scrolling gestures are installed. + Future tapNotification() async { + // Keep "tapping" the device till it responds with the string we expect, + // or throw an error instead of tying up the infrastructure for 30 minutes. + prepareForNextMessage('TAPPED'); + bool tapped = false; + int tapCount = 0; + await Future.any(>[ + () async { + while (true) { + if (tapped) { + break; + } + tapCount += 1; + print('tapping device... [$tapCount]'); + await device!.tap(100, 100); + await Future.delayed(const Duration(milliseconds: 100)); + } + }(), + () async { + print('awaiting "tapped" message... (timeout: 10 seconds)'); + try { + await receivedNextMessage?.timeout(const Duration(seconds: 10)); + } finally { + tapped = true; + } + }(), + ]); + } + /// To change the behavior of the test, override this. /// /// Make sure to call recordStart() and recordEnd() once each in that order. @@ -2070,10 +2103,11 @@ class MemoryTest { Future useMemory() async { await launchApp(); await recordStart(); + if (requiresTapToStart) { + await tapNotification(); + } prepareForNextMessage('DONE'); - print('tapping device...'); - await device!.tap(100, 100); print('awaiting "done" message...'); await receivedNextMessage; diff --git a/dev/integration_tests/flutter_gallery/test_memory/memory_nav.dart b/dev/integration_tests/flutter_gallery/test_memory/memory_nav.dart index 5992b64b1a..9b93de1310 100644 --- a/dev/integration_tests/flutter_gallery/test_memory/memory_nav.dart +++ b/dev/integration_tests/flutter_gallery/test_memory/memory_nav.dart @@ -26,7 +26,7 @@ Future main() async { final Completer ready = Completer(); runApp(GestureDetector( onTap: () { - debugPrint('Received tap.'); + debugPrint('==== MEMORY BENCHMARK ==== TAPPED ===='); ready.complete(); }, behavior: HitTestBehavior.opaque, @@ -35,12 +35,14 @@ Future main() async { ), )); await SchedulerBinding.instance.endOfFrame; - await Future.delayed(const Duration(milliseconds: 50)); debugPrint('==== MEMORY BENCHMARK ==== READY ===='); await ready.future; debugPrint('Continuing...'); + // Wait out any errant taps due to synchronization + await Future.delayed(const Duration(milliseconds: 200)); + // remove onTap handler, enable pointer events for app runApp(GestureDetector( child: const IgnorePointer(