// Copyright 2014 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:macrobenchmarks/common.dart'; import 'package:macrobenchmarks/main.dart' as app; typedef ControlCallback = Future<void> Function(WidgetController controller); class ScrollableButtonRoute { ScrollableButtonRoute(this.listViewKey, this.buttonKey); final String listViewKey; final String buttonKey; } void macroPerfTestE2E( String testName, String routeName, { Duration? pageDelay, Duration duration = const Duration(seconds: 3), ControlCallback? body, ControlCallback? setup, }) { macroPerfTestMultiPageE2E( testName, <ScrollableButtonRoute>[ ScrollableButtonRoute(kScrollableName, routeName), ], pageDelay: pageDelay, duration: duration, body: body, setup: setup, ); } void macroPerfTestMultiPageE2E( String testName, List<ScrollableButtonRoute> routes, { Duration? pageDelay, Duration duration = const Duration(seconds: 3), ControlCallback? body, ControlCallback? setup, }) { final WidgetsBinding widgetsBinding = IntegrationTestWidgetsFlutterBinding.ensureInitialized(); assert(widgetsBinding is IntegrationTestWidgetsFlutterBinding); final IntegrationTestWidgetsFlutterBinding binding = widgetsBinding as IntegrationTestWidgetsFlutterBinding; binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.benchmarkLive; testWidgets(testName, (WidgetTester tester) async { assert(tester.binding == binding); app.main(); await tester.pumpAndSettle(); // The slight initial delay avoids starting the timing during a // period of increased load on the device. Without this delay, the // benchmark has greater noise. // See: https://github.com/flutter/flutter/issues/19434 await tester.binding.delayed(const Duration(microseconds: 250)); for (final ScrollableButtonRoute route in routes) { expect(route.listViewKey, startsWith('/')); expect(route.buttonKey, startsWith('/')); // Make sure each list view page is settled await tester.pumpAndSettle(); final Finder listView = find.byKey(ValueKey<String>(route.listViewKey)); // ListView is not a Scrollable, but it contains one final Finder scrollable = find.descendant(of: listView, matching: find.byType(Scrollable)); // scrollable should find one widget as soon as the page is loaded expect(scrollable, findsOneWidget); final Finder button = find.byKey(ValueKey<String>(route.buttonKey), skipOffstage: false); // button may or may not find a widget right away until we scroll to it await tester.scrollUntilVisible(button, 50, scrollable: scrollable); // After scrolling, button should find one Widget expect(button, findsOneWidget); // Allow scrolling to settle await tester.pumpAndSettle(); await tester.tap(button); // Cannot be pumpAndSettle because some tests have infinite animation. await tester.pump(const Duration(milliseconds: 20)); } if (pageDelay != null) { // Wait for the page to load await tester.binding.delayed(pageDelay); } if (setup != null) { await setup(tester); } await binding.watchPerformance(() async { final Future<void> durationFuture = tester.binding.delayed(duration); if (body != null) { await body(tester); } await durationFuture; }); }, semanticsEnabled: false, timeout: Timeout.none); }