From 03ed06f44d8f1382d80a6c7252b1245e4ca1d189 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 28 Jun 2019 00:37:01 -0700 Subject: [PATCH] New benchmark: Gesture semantics (#35232) * Add semanticsEnabled to widgetBenchmark * Add button_matrix_app and gesture benchmark --- dev/benchmarks/microbenchmarks/README.md | 1 + .../lib/gestures/apps/button_matrix_app.dart | 52 +++++++++++++++++++ .../lib/gestures/gesture_detector_bench.dart | 52 +++++++++++++++++++ dev/devicelab/lib/tasks/microbenchmarks.dart | 1 + .../flutter_test/lib/src/widget_tester.dart | 22 ++++++-- 5 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 dev/benchmarks/microbenchmarks/lib/gestures/apps/button_matrix_app.dart create mode 100644 dev/benchmarks/microbenchmarks/lib/gestures/gesture_detector_bench.dart diff --git a/dev/benchmarks/microbenchmarks/README.md b/dev/benchmarks/microbenchmarks/README.md index 7174a5dafc..eda8baebdb 100644 --- a/dev/benchmarks/microbenchmarks/README.md +++ b/dev/benchmarks/microbenchmarks/README.md @@ -6,6 +6,7 @@ these: ``` flutter run --release lib/gestures/velocity_tracker_data.dart +flutter run --release lib/gestures/gesture_detector_bench.dart flutter run --release lib/stocks/animation_bench.dart flutter run --release lib/stocks/build_bench.dart flutter run --release lib/stocks/layout_bench.dart diff --git a/dev/benchmarks/microbenchmarks/lib/gestures/apps/button_matrix_app.dart b/dev/benchmarks/microbenchmarks/lib/gestures/apps/button_matrix_app.dart new file mode 100644 index 0000000000..1069ef941d --- /dev/null +++ b/dev/benchmarks/microbenchmarks/lib/gestures/apps/button_matrix_app.dart @@ -0,0 +1,52 @@ +// Copyright 2019 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/material.dart'; + +class ButtonMatrixApp extends StatefulWidget { + @override + ButtonMatrixAppState createState() => ButtonMatrixAppState(); +} + +class ButtonMatrixAppState extends State { + + int count = 1; + int increment = 1; + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: Text('Count: $count'), + actions: [ + FlatButton( + onPressed: () => setState(() { count += increment; }), + child: Text('Add $increment'), + ), + ], + ), + body: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: List.filled( + 3, + Column( + children: List.filled( + 10, + FlatButton( + child: const Text('Faster'), + onPressed: () => setState(() { increment += 1; }), + ), + ), + ), + ), + ), + ), + ); + } +} + +void main() { + runApp(ButtonMatrixApp()); +} diff --git a/dev/benchmarks/microbenchmarks/lib/gestures/gesture_detector_bench.dart b/dev/benchmarks/microbenchmarks/lib/gestures/gesture_detector_bench.dart new file mode 100644 index 0000000000..eba8b80aa8 --- /dev/null +++ b/dev/benchmarks/microbenchmarks/lib/gestures/gesture_detector_bench.dart @@ -0,0 +1,52 @@ +// Copyright 2019 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/gestures.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../common.dart'; +import './apps/button_matrix_app.dart' as button_matrix; + +const int _kNumWarmUpIters = 20; +const int _kNumIters = 300; + +Future main() async { + assert(false, "Don't run benchmarks in checked mode! Use 'flutter run --release'."); + final Stopwatch watch = Stopwatch(); + print('GestureDetector semantics benchmark...'); + + await benchmarkWidgets((WidgetTester tester) async { + button_matrix.main(); + await tester.pump(); + await tester.pump(const Duration(seconds: 1)); + + Future iter() async { + // Press a button to update the screen + await tester.tapAt(const Offset(760.0, 30.0)); + await tester.pump(); + } + + // Warm up runs get the app into steady state, making benchmark + // results more credible + for (int i = 0; i < _kNumWarmUpIters; i += 1) { + await iter(); + } + await tester.pumpAndSettle(); + + watch.start(); + for (int i = 0; i < _kNumIters; i += 1) { + await iter(); + } + watch.stop(); + }, semanticsEnabled: true); + + final BenchmarkResultPrinter printer = BenchmarkResultPrinter(); + printer.addResult( + description: 'GestureDetector', + value: watch.elapsedMicroseconds / _kNumIters, + unit: 'µs per iteration', + name: 'gesture_detector_bench', + ); + printer.printToStdout(); +} diff --git a/dev/devicelab/lib/tasks/microbenchmarks.dart b/dev/devicelab/lib/tasks/microbenchmarks.dart index 2436c76d8e..e3c3f35aab 100644 --- a/dev/devicelab/lib/tasks/microbenchmarks.dart +++ b/dev/devicelab/lib/tasks/microbenchmarks.dart @@ -53,6 +53,7 @@ TaskFunction createMicrobenchmarkTask() { allResults.addAll(await _runMicrobench('lib/stocks/build_bench.dart')); allResults.addAll(await _runMicrobench('lib/geometry/rrect_contains_bench.dart')); allResults.addAll(await _runMicrobench('lib/gestures/velocity_tracker_bench.dart')); + allResults.addAll(await _runMicrobench('lib/gestures/gesture_detector_bench.dart')); allResults.addAll(await _runMicrobench('lib/stocks/animation_bench.dart')); return TaskResult.success(allResults, benchmarkScoreKeys: allResults.keys.toList()); diff --git a/packages/flutter_test/lib/src/widget_tester.dart b/packages/flutter_test/lib/src/widget_tester.dart index a95f866d8f..d2fa0d604d 100644 --- a/packages/flutter_test/lib/src/widget_tester.dart +++ b/packages/flutter_test/lib/src/widget_tester.dart @@ -68,7 +68,7 @@ typedef WidgetTesterCallback = Future Function(WidgetTester widgetTester); /// In general, timeouts are race conditions and cause flakes, so best practice /// is to avoid the use of timeouts in tests. /// -/// If the `enableSemantics` parameter is set to `true`, +/// If the `semanticsEnabled` parameter is set to `true`, /// [WidgetTester.ensureSemantics] will have been called before the tester is /// passed to the `callback`, and that handle will automatically be disposed /// after the callback is finished. @@ -144,6 +144,11 @@ void testWidgets( /// If the callback is asynchronous, make sure you `await` the call /// to [benchmarkWidgets], otherwise it won't run! /// +/// If the `semanticsEnabled` parameter is set to `true`, +/// [WidgetTester.ensureSemantics] will have been called before the tester is +/// passed to the `callback`, and that handle will automatically be disposed +/// after the callback is finished. +/// /// Benchmarks must not be run in checked mode, because the performance is not /// representative. To avoid this, this function will print a big message if it /// is run in checked mode. Unit tests of this method pass `mayRunWithAsserts`, @@ -165,7 +170,11 @@ void testWidgets( /// }); /// exit(0); /// } -Future benchmarkWidgets(WidgetTesterCallback callback, {bool mayRunWithAsserts = false}) { +Future benchmarkWidgets( + WidgetTesterCallback callback, { + bool mayRunWithAsserts = false, + bool semanticsEnabled = true, +}) { assert(() { if (mayRunWithAsserts) return true; @@ -186,9 +195,16 @@ Future benchmarkWidgets(WidgetTesterCallback callback, {bool mayRunWithAss final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized(); assert(binding is! AutomatedTestWidgetsFlutterBinding); final WidgetTester tester = WidgetTester._(binding); + SemanticsHandle semanticsHandle; + if (semanticsEnabled == true) { + semanticsHandle = tester.ensureSemantics(); + } tester._recordNumberOfSemanticsHandles(); return binding.runTest( - () => callback(tester), + () async { + await callback(tester); + semanticsHandle?.dispose(); + }, tester._endOfTestVerifications, ) ?? Future.value(); }