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
This commit is contained in:
John McDole 2024-06-17 10:14:47 -07:00 committed by GitHub
parent 7960958327
commit e80bbd929e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 47 additions and 11 deletions

View file

@ -23,7 +23,7 @@ Future<void> main() async {
final Completer<void> ready = Completer<void>();
runApp(GestureDetector(
onTap: () {
debugPrint('Received tap.');
debugPrint('==== MEMORY BENCHMARK ==== TAPPED ====');
ready.complete();
},
behavior: HitTestBehavior.opaque,
@ -32,16 +32,14 @@ Future<void> 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<void>.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<void>.delayed(const Duration(milliseconds: 200));
// remove onTap handler, enable pointer events for app
runApp(GestureDetector(
child: const IgnorePointer(

View file

@ -13,5 +13,6 @@ Future<void> main() async {
'${flutterDirectory.path}/dev/benchmarks/complex_layout',
'test_memory/scroll_perf.dart',
'com.yourcompany.complexLayout',
requiresTapToStart: true,
).run);
}

View file

@ -11,5 +11,6 @@ Future<void> main() async {
'${flutterDirectory.path}/dev/integration_tests/flutter_gallery',
'test_memory/memory_nav.dart',
'io.flutter.demo.gallery',
requiresTapToStart: true,
).run);
}

View file

@ -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<void> 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(<Future<void>>[
() async {
while (true) {
if (tapped) {
break;
}
tapCount += 1;
print('tapping device... [$tapCount]');
await device!.tap(100, 100);
await Future<void>.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<void> 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;

View file

@ -26,7 +26,7 @@ Future<void> main() async {
final Completer<void> ready = Completer<void>();
runApp(GestureDetector(
onTap: () {
debugPrint('Received tap.');
debugPrint('==== MEMORY BENCHMARK ==== TAPPED ====');
ready.complete();
},
behavior: HitTestBehavior.opaque,
@ -35,12 +35,14 @@ Future<void> main() async {
),
));
await SchedulerBinding.instance.endOfFrame;
await Future<void>.delayed(const Duration(milliseconds: 50));
debugPrint('==== MEMORY BENCHMARK ==== READY ====');
await ready.future;
debugPrint('Continuing...');
// Wait out any errant taps due to synchronization
await Future<void>.delayed(const Duration(milliseconds: 200));
// remove onTap handler, enable pointer events for app
runApp(GestureDetector(
child: const IgnorePointer(