diff --git a/dev/benchmarks/complex_layout/test_memory/scroll_perf.dart b/dev/benchmarks/complex_layout/test_memory/scroll_perf.dart deleted file mode 100644 index c42f8b005b5..00000000000 --- a/dev/benchmarks/complex_layout/test_memory/scroll_perf.dart +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2016 The Chromium 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 'dart:async'; - -import 'package:complex_layout/main.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; -import 'package:flutter_test/flutter_test.dart'; - -/// The speed, in pixels per second, that the drag gestures should end with. -const double speed = 1500.0; - -/// The number of down drags and the number of up drags. The total number of -/// gestures is twice this number. -const int maxIterations = 4; - -/// The time that is allowed between gestures for the fling effect to settle. -const Duration pauses = Duration(milliseconds: 500); - -Future main() async { - final Completer ready = new Completer(); - runApp(new GestureDetector( - onTap: () { - debugPrint('Received tap.'); - ready.complete(); - }, - behavior: HitTestBehavior.opaque, - child: new IgnorePointer( - ignoring: true, - child: new ComplexLayoutApp(), - ), - )); - await SchedulerBinding.instance.endOfFrame; - - /// Wait 50ms to allow the GPU thread to actually put up the frame. (The - /// endOfFrame future ends when we send the data to the engine, before the GPU - /// thread has had a chance to rasterize, etc.) - await new Future.delayed(const Duration(milliseconds: 50)); - debugPrint('==== MEMORY BENCHMARK ==== READY ===='); - - await ready.future; // waits for tap sent by devicelab task - debugPrint('Continuing...'); - - // remove onTap handler, enable pointer events for app - runApp(new GestureDetector( - child: new IgnorePointer( - ignoring: false, - child: new ComplexLayoutApp(), - ), - )); - await SchedulerBinding.instance.endOfFrame; - - final WidgetController controller = new LiveWidgetController(WidgetsBinding.instance); - - // Scroll down - for (int iteration = 0; iteration < maxIterations; iteration += 1) { - debugPrint('Scroll down... $iteration/$maxIterations'); - await controller.fling(find.byType(ListView), const Offset(0.0, -700.0), speed); - await new Future.delayed(pauses); - } - - // Scroll up - for (int iteration = 0; iteration < maxIterations; iteration += 1) { - debugPrint('Scroll up... $iteration/$maxIterations'); - await controller.fling(find.byType(ListView), const Offset(0.0, 300.0), speed); - await new Future.delayed(pauses); - } - - debugPrint('==== MEMORY BENCHMARK ==== DONE ===='); -} 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 1729629efd8..ff68529f472 100644 --- a/dev/devicelab/bin/tasks/complex_layout_scroll_perf__memory.dart +++ b/dev/devicelab/bin/tasks/complex_layout_scroll_perf__memory.dart @@ -4,16 +4,11 @@ import 'dart:async'; +import 'package:flutter_devicelab/tasks/perf_tests.dart'; import 'package:flutter_devicelab/framework/adb.dart'; import 'package:flutter_devicelab/framework/framework.dart'; -import 'package:flutter_devicelab/framework/utils.dart'; -import 'package:flutter_devicelab/tasks/perf_tests.dart'; Future main() async { deviceOperatingSystem = DeviceOperatingSystem.android; - await task(new MemoryTest( - '${flutterDirectory.path}/dev/benchmarks/complex_layout', - 'test_memory/scroll_perf.dart', - 'com.yourcompany.complexLayout', - ).run); + await task(createComplexLayoutScrollMemoryTest()); } diff --git a/dev/devicelab/bin/tasks/flutter_gallery__back_button_memory.dart b/dev/devicelab/bin/tasks/flutter_gallery__back_button_memory.dart index f66573015a8..a7880307a8f 100644 --- a/dev/devicelab/bin/tasks/flutter_gallery__back_button_memory.dart +++ b/dev/devicelab/bin/tasks/flutter_gallery__back_button_memory.dart @@ -2,57 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -/// Measure application memory usage after pausing and resuming the app -/// with the Android back button. - import 'dart:async'; -import 'package:flutter_devicelab/framework/adb.dart'; -import 'package:flutter_devicelab/framework/framework.dart'; -import 'package:flutter_devicelab/framework/utils.dart'; import 'package:flutter_devicelab/tasks/perf_tests.dart'; - -const String packageName = 'io.flutter.demo.gallery'; -const String activityName = 'io.flutter.demo.gallery.MainActivity'; - -class BackButtonMemoryTest extends MemoryTest { - BackButtonMemoryTest() : super('${flutterDirectory.path}/examples/flutter_gallery', 'test_memory/back_button.dart', packageName); - - @override - AndroidDevice get device => super.device; - - /// Perform a series of back button suspend and resume cycles. - @override - Future useMemory() async { - await launchApp(); - await recordStart(); - for (int iteration = 0; iteration < 10; iteration += 1) { - print('back/forward iteration $iteration'); - - // Push back button, wait for it to be seen by the Flutter app. - prepareForNextMessage('AppLifecycleState.paused'); - await device.shellExec('input', ['keyevent', 'KEYCODE_BACK']); - await receivedNextMessage; - - // Give Android time to settle (e.g. run GCs) after closing the app. - await new Future.delayed(const Duration(milliseconds: 100)); - - // Relaunch the app, wait for it to launch. - prepareForNextMessage('READY'); - final String output = await device.shellEval('am', ['start', '-n', '$packageName/$activityName']); - print('adb shell am start: $output'); - if (output.contains('Error')) - fail('unable to launch activity'); - await receivedNextMessage; - - // Wait for the Flutter app to settle (e.g. run GCs). - await new Future.delayed(const Duration(milliseconds: 100)); - } - await recordEnd(); - } -} +import 'package:flutter_devicelab/framework/framework.dart'; Future main() async { - deviceOperatingSystem = DeviceOperatingSystem.android; - await task(new BackButtonMemoryTest().run); + await task(createGalleryBackButtonMemoryTest()); } diff --git a/dev/devicelab/bin/tasks/flutter_gallery__memory_nav.dart b/dev/devicelab/bin/tasks/flutter_gallery__memory_nav.dart index 8c2b301e97e..90217afb344 100644 --- a/dev/devicelab/bin/tasks/flutter_gallery__memory_nav.dart +++ b/dev/devicelab/bin/tasks/flutter_gallery__memory_nav.dart @@ -4,14 +4,9 @@ import 'dart:async'; -import 'package:flutter_devicelab/framework/framework.dart'; -import 'package:flutter_devicelab/framework/utils.dart'; import 'package:flutter_devicelab/tasks/perf_tests.dart'; +import 'package:flutter_devicelab/framework/framework.dart'; Future main() async { - await task(new MemoryTest( - '${flutterDirectory.path}/examples/flutter_gallery', - 'test_memory/memory_nav.dart', - 'io.flutter.demo.gallery', - ).run); + await task(createGalleryNavigationMemoryTest()); } diff --git a/dev/devicelab/bin/tasks/hello_world__memory.dart b/dev/devicelab/bin/tasks/hello_world__memory.dart index f3a99aebc61..16898ac77c7 100644 --- a/dev/devicelab/bin/tasks/hello_world__memory.dart +++ b/dev/devicelab/bin/tasks/hello_world__memory.dart @@ -4,36 +4,9 @@ import 'dart:async'; -import 'package:flutter_devicelab/framework/framework.dart'; -import 'package:flutter_devicelab/framework/utils.dart'; import 'package:flutter_devicelab/tasks/perf_tests.dart'; - -class HelloWorldMemoryTest extends MemoryTest { - HelloWorldMemoryTest() : super( - '${flutterDirectory.path}/examples/hello_world', - 'lib/main.dart', - 'io.flutter.examples.hello_world', - ); - - /// Launch an app with no instrumentation and measure its memory usage after - /// 1.5s and 3.0s. - @override - Future useMemory() async { - print('launching $project$test on device...'); - await flutter('run', options: [ - '--verbose', - '--release', - '--no-resident', - '-d', device.deviceId, - test, - ]); - await new Future.delayed(const Duration(milliseconds: 1500)); - await recordStart(); - await new Future.delayed(const Duration(milliseconds: 3000)); - await recordEnd(); - } -} +import 'package:flutter_devicelab/framework/framework.dart'; Future main() async { - await task(new HelloWorldMemoryTest().run); + await task(createHelloWorldMemoryTest()); } diff --git a/dev/devicelab/lib/framework/adb.dart b/dev/devicelab/lib/framework/adb.dart index 6849e03dd62..de65020a0fd 100644 --- a/dev/devicelab/lib/framework/adb.dart +++ b/dev/devicelab/lib/framework/adb.dart @@ -83,18 +83,9 @@ abstract class Device { /// Assumes the device doesn't have a secure unlock pattern. Future unlock(); - /// Emulate a tap on the touch screen. - Future tap(int x, int y); - /// Read memory statistics for a process. Future> getMemoryStats(String packageName); - /// Stream the system log from the device. - /// - /// Flutter applications' `print` statements end up in this log - /// with some prefix. - Stream get logcat; - /// Stop a process. Future stop(String packageName); } @@ -246,11 +237,6 @@ class AndroidDevice implements Device { await shellExec('input', const ['keyevent', '82']); } - @override - Future tap(int x, int y) async { - await shellExec('input', ['tap', '$x', '$y']); - } - /// Retrieves device's wakefulness state. /// /// See: https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/os/PowerManagerInternal.java @@ -285,63 +271,6 @@ class AndroidDevice implements Device { }; } - @override - Stream get logcat { - final Completer stdoutDone = new Completer(); - final Completer stderrDone = new Completer(); - final Completer processDone = new Completer(); - final Completer abort = new Completer(); - bool aborted = false; - StreamController stream; - stream = new StreamController( - onListen: () async { - await adb(['logcat', '--clear']); - final Process process = await startProcess(adbPath, ['-s', deviceId, 'logcat']); - process.stdout - .transform(utf8.decoder) - .transform(const LineSplitter()) - .listen((String line) { - print('adb logcat: $line'); - stream.sink.add(line); - }, onDone: () { stdoutDone.complete(); }); - process.stderr - .transform(utf8.decoder) - .transform(const LineSplitter()) - .listen((String line) { - print('adb logcat stderr: $line'); - }, onDone: () { stderrDone.complete(); }); - process.exitCode.then((int exitCode) { - print('adb logcat process terminated with exit code $exitCode'); - if (!aborted) { - stream.addError(BuildFailedError('adb logcat failed with exit code $exitCode.')); - processDone.complete(); - } - }); - await Future.any(>[ - Future.wait(>[ - stdoutDone.future, - stderrDone.future, - processDone.future, - ]), - abort.future, - ]); - aborted = true; - print('terminating adb logcat'); - process.kill(); - print('closing logcat stream'); - await stream.close(); - }, - onCancel: () { - if (!aborted) { - print('adb logcat aborted'); - aborted = true; - abort.complete(); - } - }, - ); - return stream.stream; - } - @override Future stop(String packageName) async { return shellExec('am', ['force-stop', packageName]); @@ -442,21 +371,11 @@ class IosDevice implements Device { @override Future unlock() async {} - @override - Future tap(int x, int y) async { - throw 'Not implemented'; - } - @override Future> getMemoryStats(String packageName) async { throw 'Not implemented'; } - @override - Stream get logcat { - throw 'Not implemented'; - } - @override Future stop(String packageName) async {} } diff --git a/dev/devicelab/lib/framework/framework.dart b/dev/devicelab/lib/framework/framework.dart index 3ad25f5118b..aa6c3a81410 100644 --- a/dev/devicelab/lib/framework/framework.dart +++ b/dev/devicelab/lib/framework/framework.dart @@ -81,15 +81,12 @@ class _TaskRunner { Future run(Duration taskTimeout) async { try { _taskStarted = true; - print('Running task.'); final TaskResult result = await _performTask().timeout(taskTimeout); _completer.complete(result); return result; } on TimeoutException catch (_) { - print('Task timed out after $taskTimeout.'); return new TaskResult.failure('Task timed out after $taskTimeout'); } finally { - print('Cleaning up after task...'); await forceQuitRunningProcesses(); _closeKeepAlivePort(); } diff --git a/dev/devicelab/lib/framework/utils.dart b/dev/devicelab/lib/framework/utils.dart index 3263000b70d..fc3a7779b11 100644 --- a/dev/devicelab/lib/framework/utils.dart +++ b/dev/devicelab/lib/framework/utils.dart @@ -244,7 +244,7 @@ Future forceQuitRunningProcesses() async { // Whatever's left, kill it. for (ProcessInfo p in _runningProcesses) { - print('Force-quitting process:\n$p'); + print('Force quitting process:\n$p'); if (!p.process.kill()) { print('Failed to force quit process'); } @@ -528,6 +528,8 @@ int parseServicePort(String line, { // e.g. "An Observatory debugger and profiler on ... is available at: http://127.0.0.1:8100/" final RegExp pattern = new RegExp('$prefix(\\S+:(\\d+)/\\S*)\$', multiLine: multiLine); final Match match = pattern.firstMatch(line); + print(pattern); + print(match); return match == null ? null : int.parse(match.group(2)); } diff --git a/dev/devicelab/lib/tasks/perf_tests.dart b/dev/devicelab/lib/tasks/perf_tests.dart index 0d31ef63ff3..15fca3a0588 100644 --- a/dev/devicelab/lib/tasks/perf_tests.dart +++ b/dev/devicelab/lib/tasks/perf_tests.dart @@ -7,7 +7,7 @@ import 'dart:convert' show json; import 'dart:io'; import 'package:meta/meta.dart'; -import 'package:path/path.dart' as path; +import 'package:path/path.dart' as p; import '../framework/adb.dart'; import '../framework/framework.dart'; @@ -30,6 +30,14 @@ TaskFunction createTilesScrollPerfTest() { ).run; } +TaskFunction createComplexLayoutScrollMemoryTest() { + return new MemoryTest( + '${flutterDirectory.path}/dev/benchmarks/complex_layout', + 'com.yourcompany.complexLayout', + testTarget: 'test_driver/scroll_perf.dart', + ).run; +} + TaskFunction createFlutterGalleryStartupTest() { return new StartupTest( '${flutterDirectory.path}/examples/flutter_gallery', @@ -54,6 +62,29 @@ TaskFunction createComplexLayoutCompileTest() { return new CompileTest('${flutterDirectory.path}/dev/benchmarks/complex_layout').run; } +TaskFunction createHelloWorldMemoryTest() { + return new MemoryTest( + '${flutterDirectory.path}/examples/hello_world', + 'io.flutter.examples.hello_world', + ).run; +} + +TaskFunction createGalleryNavigationMemoryTest() { + return new MemoryTest( + '${flutterDirectory.path}/examples/flutter_gallery', + 'io.flutter.demo.gallery', + testTarget: 'test_driver/memory_nav.dart', + ).run; +} + +TaskFunction createGalleryBackButtonMemoryTest() { + return new AndroidBackButtonMemoryTest( + '${flutterDirectory.path}/examples/flutter_gallery', + 'io.flutter.demo.gallery', + 'io.flutter.demo.gallery.MainActivity', + ).run; +} + TaskFunction createFlutterViewStartupTest() { return new StartupTest( '${flutterDirectory.path}/examples/flutter_view', @@ -335,15 +366,15 @@ class CompileTest { static Future> getSizesFromIosApp(String appPath) async { // Thin the binary to only contain one architecture. - final String xcodeBackend = path.join(flutterDirectory.path, 'packages', 'flutter_tools', 'bin', 'xcode_backend.sh'); + final String xcodeBackend = p.join(flutterDirectory.path, 'packages', 'flutter_tools', 'bin', 'xcode_backend.sh'); await exec(xcodeBackend, ['thin'], environment: { 'ARCHS': 'arm64', - 'WRAPPER_NAME': path.basename(appPath), - 'TARGET_BUILD_DIR': path.dirname(appPath), + 'WRAPPER_NAME': p.basename(appPath), + 'TARGET_BUILD_DIR': p.dirname(appPath), }); - final File appFramework = new File(path.join(appPath, 'Frameworks', 'App.framework', 'App')); - final File flutterFramework = new File(path.join(appPath, 'Frameworks', 'Flutter.framework', 'Flutter')); + final File appFramework = new File(p.join(appPath, 'Frameworks', 'App.framework', 'App')); + final File flutterFramework = new File(p.join(appPath, 'Frameworks', 'Flutter.framework', 'Flutter')); return { 'app_framework_uncompressed_bytes': await appFramework.length(), @@ -389,167 +420,123 @@ class CompileTest { /// Measure application memory usage. class MemoryTest { - MemoryTest(this.project, this.test, this.package); + const MemoryTest(this.testDirectory, this.packageName, { this.testTarget }); - final String project; - final String test; - final String package; + final String testDirectory; + final String packageName; - /// Completes when the log line specified in the last call to - /// [prepareForNextMessage] is seen by `adb logcat`. - Future get receivedNextMessage => _receivedNextMessage?.future; - Completer _receivedNextMessage; - String _nextMessage; - - /// Prepares the [receivedNextMessage] future such that it will complete - /// when `adb logcat` sees a log line with the given `message`. - void prepareForNextMessage(String message) { - _nextMessage = message; - _receivedNextMessage = new Completer(); - } - - int get iterationCount => 15; - - Device get device => _device; - Device _device; + /// Path to a flutter driver script that will run after starting the app. + /// + /// If not specified, then the test will start the app, gather statistics, and then exit. + final String testTarget; Future run() { - return inDirectory(project, () async { - // This test currently only works on Android, because device.logcat, - // device.getMemoryStats, etc, aren't implemented for iOS. - - _device = await devices.workingDevice; + return inDirectory(testDirectory, () async { + final Device device = await devices.workingDevice; await device.unlock(); + final String deviceId = device.deviceId; await flutter('packages', options: ['get']); if (deviceOperatingSystem == DeviceOperatingSystem.ios) - await prepareProvisioningCertificates(project); + await prepareProvisioningCertificates(testDirectory); - final StreamSubscription adb = device.logcat.listen( - (String data) { - if (data.contains('==== MEMORY BENCHMARK ==== $_nextMessage ====')) - _receivedNextMessage.complete(); - }, - ); + final List runOptions = [ + '-v', + '--profile', + '--trace-startup', // wait for the first frame to render + '-d', + deviceId, + '--observatory-port', + '0', + ]; + if (testTarget != null) + runOptions.addAll(['-t', testTarget]); + final String output = await evalFlutter('run', options: runOptions); + final int observatoryPort = parseServicePort(output, prefix: 'Successfully connected to service protocol: ', multiLine: true); + if (observatoryPort == null) + throw new Exception('Could not find observatory port in "flutter run" output.'); - for (int iteration = 0; iteration < iterationCount; iteration += 1) { - print('running memory test iteration $iteration...'); - _startMemoryUsage = null; - await useMemory(); - assert(_startMemoryUsage != null); - assert(_startMemory.length == iteration + 1); - assert(_endMemory.length == iteration + 1); - assert(_diffMemory.length == iteration + 1); - print('terminating...'); - await device.stop(package); - await new Future.delayed(const Duration(milliseconds: 10)); + final Map startData = await device.getMemoryStats(packageName); + + final Map data = { + 'start_total_kb': startData['total_kb'], + }; + + if (testTarget != null) { + await flutter('drive', options: [ + '-v', + '-t', + testTarget, + '-d', + deviceId, + '--use-existing-app=http://localhost:$observatoryPort', + ]); + + final Map endData = await device.getMemoryStats(packageName); + data['end_total_kb'] = endData['total_kb']; + data['diff_total_kb'] = endData['total_kb'] - startData['total_kb']; } - await adb.cancel(); + await device.stop(packageName); - final ListStatistics startMemoryStatistics = new ListStatistics(_startMemory); - final ListStatistics endMemoryStatistics = new ListStatistics(_endMemory); - final ListStatistics diffMemoryStatistics = new ListStatistics(_diffMemory); - - final Map memoryUsage = {}; - memoryUsage.addAll(startMemoryStatistics.asMap('start')); - memoryUsage.addAll(endMemoryStatistics.asMap('end')); - memoryUsage.addAll(diffMemoryStatistics.asMap('diff')); - - _device = null; - _startMemory.clear(); - _endMemory.clear(); - _diffMemory.clear(); - - return new TaskResult.success(memoryUsage, benchmarkScoreKeys: memoryUsage.keys.toList()); + return new TaskResult.success(data, benchmarkScoreKeys: data.keys.toList()); }); } - - /// Starts the app specified by [test] on the [device]. - /// - /// The [run] method will terminate it by its package name ([package]). - Future launchApp() async { - prepareForNextMessage('READY'); - print('launching $project$test on device...'); - await flutter('run', options: [ - '--verbose', - '--release', - '--no-resident', - '-d', device.deviceId, - test, - ]); - print('awaiting "ready" message...'); - await receivedNextMessage; - } - - /// To change the behaviour of the test, override this. - /// - /// Make sure to call recordStart() and recordEnd() once each in that order. - /// - /// By default it just launches the app, records memory usage, taps the device, - /// awaits a DONE notification, and records memory usage again. - Future useMemory() async { - await launchApp(); - await recordStart(); - - prepareForNextMessage('DONE'); - print('tapping device...'); - await device.tap(100, 100); - print('awaiting "done" message...'); - await receivedNextMessage; - - await recordEnd(); - } - - final List _startMemory = []; - final List _endMemory = []; - final List _diffMemory = []; - - Map _startMemoryUsage; - - @protected - Future recordStart() async { - assert(_startMemoryUsage == null); - print('snapshotting memory usage...'); - _startMemoryUsage = await device.getMemoryStats(package); - } - - @protected - Future recordEnd() async { - assert(_startMemoryUsage != null); - print('snapshotting memory usage...'); - final Map endMemoryUsage = await device.getMemoryStats(package); - _startMemory.add(_startMemoryUsage['total_kb']); - _endMemory.add(endMemoryUsage['total_kb']); - _diffMemory.add(endMemoryUsage['total_kb'] - _startMemoryUsage['total_kb']); - } } -/// Holds simple statistics of an odd-lengthed list of integers. -class ListStatistics { - factory ListStatistics(Iterable data) { - assert(data.isNotEmpty); - assert(data.length % 2 == 1); - final List sortedData = data.toList()..sort(); - return new ListStatistics._( - sortedData.first, - sortedData.last, - sortedData[(sortedData.length - 1) ~/ 2], - ); - } +/// Measure application memory usage after pausing and resuming the app +/// with the Android back button. +class AndroidBackButtonMemoryTest { + const AndroidBackButtonMemoryTest(this.testDirectory, this.packageName, this.activityName); - const ListStatistics._(this.min, this.max, this.median); + final String testDirectory; + final String packageName; + final String activityName; - final int min; - final int max; - final int median; + Future run() { + return inDirectory(testDirectory, () async { + if (deviceOperatingSystem != DeviceOperatingSystem.android) { + throw 'This test is only supported on Android'; + } - Map asMap(String prefix) { - return { - '$prefix-min': min, - '$prefix-max': max, - '$prefix-median': median, - }; + final AndroidDevice device = await devices.workingDevice; + await device.unlock(); + final String deviceId = device.deviceId; + await flutter('packages', options: ['get']); + + await flutter('run', options: [ + '-v', + '--profile', + '--trace-startup', // wait for the first frame to render + '-d', + deviceId, + ]); + + final Map startData = await device.getMemoryStats(packageName); + + final Map data = { + 'start_total_kb': startData['total_kb'], + }; + + // Perform a series of back button suspend and resume cycles. + for (int i = 0; i < 10; i++) { + await device.shellExec('input', ['keyevent', 'KEYCODE_BACK']); + await new Future.delayed(const Duration(milliseconds: 1000)); + final String output = await device.shellEval('am', ['start', '-n', '$packageName/$activityName']); + print(output); + if (output.contains('Error')) + return new TaskResult.failure('unable to launch activity'); + await new Future.delayed(const Duration(milliseconds: 1000)); + } + + final Map endData = await device.getMemoryStats(packageName); + data['end_total_kb'] = endData['total_kb']; + data['diff_total_kb'] = endData['total_kb'] - startData['total_kb']; + + await device.stop(packageName); + + return new TaskResult.success(data, benchmarkScoreKeys: data.keys.toList()); + }); } } diff --git a/dev/devicelab/test/adb_test.dart b/dev/devicelab/test/adb_test.dart index 82d6842353b..6cc8dc8b3fa 100644 --- a/dev/devicelab/test/adb_test.dart +++ b/dev/devicelab/test/adb_test.dart @@ -93,15 +93,6 @@ void main() { ]); }); }); - - group('adb', () { - test('tap', () async { - await device.tap(100, 200); - expectLog([ - cmd(command: 'input', arguments: ['tap', '100', '200']), - ]); - }); - }); }); } diff --git a/examples/flutter_gallery/lib/gallery/home.dart b/examples/flutter_gallery/lib/gallery/home.dart index e7dda503a6b..0e8826e3b46 100644 --- a/examples/flutter_gallery/lib/gallery/home.dart +++ b/examples/flutter_gallery/lib/gallery/home.dart @@ -363,7 +363,7 @@ class _GalleryHomeState extends State with SingleTickerProviderStat ? const Text('Flutter gallery') : new Text(_category.name), ), - frontHeading: widget.testMode ? null : new Container(height: 24.0), + frontHeading: widget.testMode ? null: new Container(height: 24.0), frontLayer: new AnimatedSwitcher( duration: _kFrontLayerSwitchDuration, switchOutCurve: switchOutCurve, diff --git a/examples/flutter_gallery/test/live_smoketest.dart b/examples/flutter_gallery/test/live_smoketest.dart index 2f080aadd9b..13afb6fad5f 100644 --- a/examples/flutter_gallery/test/live_smoketest.dart +++ b/examples/flutter_gallery/test/live_smoketest.dart @@ -44,7 +44,7 @@ Future main() async { fail('Unrecognized demo names in _kSkippedDemoTitles: $_kSkippedDemoTitles'); runApp(const GalleryApp(testMode: true)); - final _LiveWidgetController controller = new _LiveWidgetController(WidgetsBinding.instance); + final _LiveWidgetController controller = new _LiveWidgetController(); for (GalleryDemoCategory category in kAllGalleryDemoCategories) { await controller.tap(find.text(category.name)); for (GalleryDemo demo in kGalleryCategoryToDemos[category]) { @@ -73,8 +73,9 @@ Future main() async { } } -class _LiveWidgetController extends LiveWidgetController { - _LiveWidgetController(WidgetsBinding binding) : super(binding); +class _LiveWidgetController { + + final WidgetController _controller = new WidgetController(WidgetsBinding.instance); /// With [frameSync] enabled, Flutter Driver will wait to perform an action /// until there are no pending frames in the app under test. @@ -106,9 +107,8 @@ class _LiveWidgetController extends LiveWidgetController { return finder; } - @override - Future tap(Finder finder, { int pointer }) async { - await tap(await _waitForElement(finder), pointer: pointer); + Future tap(Finder finder) async { + await _controller.tap(await _waitForElement(finder)); } Future scrollIntoView(Finder finder, {double alignment}) async { diff --git a/examples/flutter_gallery/test_driver/memory_nav.dart b/examples/flutter_gallery/test_driver/memory_nav.dart new file mode 100644 index 00000000000..42b8e372e2d --- /dev/null +++ b/examples/flutter_gallery/test_driver/memory_nav.dart @@ -0,0 +1,15 @@ +// Copyright 2016 The Chromium 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_driver/driver_extension.dart'; +import 'package:flutter_gallery/gallery/app.dart' show GalleryApp; +import 'package:flutter/material.dart'; + +void main() { + enableFlutterDriverExtension(); + // As in lib/main.dart: overriding https://github.com/flutter/flutter/issues/13736 + // for better visual effect at the cost of performance. + MaterialPageRoute.debugEnableFadingRoutes = true; // ignore: deprecated_member_use + runApp(const GalleryApp(testMode: true)); +} diff --git a/examples/flutter_gallery/test_driver/memory_nav_test.dart b/examples/flutter_gallery/test_driver/memory_nav_test.dart new file mode 100644 index 00000000000..8771a2fc369 --- /dev/null +++ b/examples/flutter_gallery/test_driver/memory_nav_test.dart @@ -0,0 +1,32 @@ +import 'package:flutter_driver/flutter_driver.dart'; +import 'package:test/test.dart'; + +void main() { + group('flutter gallery transitions', () { + FlutterDriver driver; + setUpAll(() async { + driver = await FlutterDriver.connect(); + }); + + tearDownAll(() async { + if (driver != null) + await driver.close(); + }); + + test('navigation', () async { + await driver.tap(find.text('Material')); + + final SerializableFinder demoList = find.byValueKey('GalleryDemoList'); + final SerializableFinder demoItem = find.text('Text fields'); + await driver.scrollUntilVisible(demoList, demoItem, + dyScroll: -300.0, + alignment: 0.5, + timeout: const Duration(minutes: 1), + ); + for (int i = 0; i < 15; i++) { + await driver.tap(demoItem); + await driver.tap(find.byTooltip('Back')); + } + }); + }); +} diff --git a/examples/flutter_gallery/test_memory/back_button.dart b/examples/flutter_gallery/test_memory/back_button.dart deleted file mode 100644 index 0eeb307ec3a..00000000000 --- a/examples/flutter_gallery/test_memory/back_button.dart +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// See //dev/devicelab/bin/tasks/flutter_gallery__memory_nav.dart - -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; -import 'package:flutter_gallery/gallery/app.dart' show GalleryApp; -import 'package:flutter_test/flutter_test.dart'; - -Future endOfAnimation() async { - do { - await SchedulerBinding.instance.endOfFrame; - } while (SchedulerBinding.instance.hasScheduledFrame); -} - -int iteration = 0; - -class LifecycleObserver extends WidgetsBindingObserver { - @override - void didChangeAppLifecycleState(AppLifecycleState state) { - debugPrint('==== MEMORY BENCHMARK ==== $state ===='); - debugPrint('This was lifecycle event number $iteration in this instance'); - } -} - -Future main() async { - MaterialPageRoute.debugEnableFadingRoutes = true; // ignore: deprecated_member_use - runApp(const GalleryApp()); - await endOfAnimation(); - await new Future.delayed(const Duration(milliseconds: 50)); - debugPrint('==== MEMORY BENCHMARK ==== READY ===='); - WidgetsBinding.instance.addObserver(new LifecycleObserver()); -} diff --git a/examples/flutter_gallery/test_memory/memory_nav.dart b/examples/flutter_gallery/test_memory/memory_nav.dart deleted file mode 100644 index cce1dc9eeec..00000000000 --- a/examples/flutter_gallery/test_memory/memory_nav.dart +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// See //dev/devicelab/bin/tasks/flutter_gallery__memory_nav.dart - -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; -import 'package:flutter_gallery/gallery/app.dart' show GalleryApp; -import 'package:flutter_test/flutter_test.dart'; - -Future endOfAnimation() async { - do { - await SchedulerBinding.instance.endOfFrame; - } while (SchedulerBinding.instance.hasScheduledFrame); -} - -Future main() async { - MaterialPageRoute.debugEnableFadingRoutes = true; // ignore: deprecated_member_use - final Completer ready = new Completer(); - runApp(new GestureDetector( - onTap: () { - debugPrint('Received tap.'); - ready.complete(); - }, - behavior: HitTestBehavior.opaque, - child: const IgnorePointer( - ignoring: true, - child: GalleryApp(testMode: true), - ), - )); - await SchedulerBinding.instance.endOfFrame; - await new Future.delayed(const Duration(milliseconds: 50)); - debugPrint('==== MEMORY BENCHMARK ==== READY ===='); - - await ready.future; - debugPrint('Continuing...'); - - // remove onTap handler, enable pointer events for app - runApp(new GestureDetector( - child: const IgnorePointer( - ignoring: false, - child: GalleryApp(testMode: true), - ), - )); - await SchedulerBinding.instance.endOfFrame; - - final WidgetController controller = new LiveWidgetController(WidgetsBinding.instance); - - debugPrint('Navigating...'); - await controller.tap(find.text('Material')); - await new Future.delayed(const Duration(milliseconds: 150)); - final Finder demoList = find.byKey(const Key('GalleryDemoList')); - final Finder demoItem = find.text('Text fields'); - do { - await controller.drag(demoList, const Offset(0.0, -300.0)); - await new Future.delayed(const Duration(milliseconds: 20)); - } while (!demoItem.precache()); - - for (int iteration = 0; iteration < 15; iteration += 1) { - debugPrint('Tapping... (iteration $iteration)'); - await controller.tap(demoItem); - await endOfAnimation(); - debugPrint('Backing out...'); - await controller.tap(find.byTooltip('Back').last); - await endOfAnimation(); - } - - debugPrint('==== MEMORY BENCHMARK ==== DONE ===='); -} diff --git a/packages/flutter_driver/lib/src/driver/driver.dart b/packages/flutter_driver/lib/src/driver/driver.dart index 3626016acbf..a2dc0ad653e 100644 --- a/packages/flutter_driver/lib/src/driver/driver.dart +++ b/packages/flutter_driver/lib/src/driver/driver.dart @@ -447,19 +447,19 @@ class FlutterDriver { /// ensure the item's final position matches [alignment]. /// /// The [scrollable] must locate the scrolling widget that contains [item]. - /// Typically `find.byType('ListView')` or `find.byType('CustomScrollView')`. + /// Typically `find.byType('ListView') or `find.byType('CustomScrollView')`. /// - /// At least one of [dxScroll] and [dyScroll] must be non-zero. + /// Atleast one of [dxScroll] and [dyScroll] must be non-zero. /// /// If [item] is below the currently visible items, then specify a negative /// value for [dyScroll] that's a small enough increment to expose [item] /// without potentially scrolling it up and completely out of view. Similarly - /// if [item] is above, then specify a positive value for [dyScroll]. + /// if [item] is above, then specify a positve value for [dyScroll]. /// - /// If [item] is to the right of the currently visible items, then + /// If [item] is to the right of the the currently visible items, then /// specify a negative value for [dxScroll] that's a small enough increment to /// expose [item] without potentially scrolling it up and completely out of - /// view. Similarly if [item] is to the left, then specify a positive value + /// view. Similarly if [item] is to the left, then specify a positve value /// for [dyScroll]. /// /// The [timeout] value should be long enough to accommodate as many scrolls @@ -483,7 +483,7 @@ class FlutterDriver { // the chance to complete if the item is already onscreen; if not, scroll // repeatedly until we either find the item or time out. bool isVisible = false; - waitFor(item, timeout: timeout).then((Null value) { isVisible = true; }); + waitFor(item, timeout: timeout).then((Null _) { isVisible = true; }); await new Future.delayed(const Duration(milliseconds: 500)); while (!isVisible) { await scroll(scrollable, dxScroll, dyScroll, const Duration(milliseconds: 100)); diff --git a/packages/flutter_driver/lib/src/extension/extension.dart b/packages/flutter_driver/lib/src/extension/extension.dart index 2043ed5bd71..78584c25933 100644 --- a/packages/flutter_driver/lib/src/extension/extension.dart +++ b/packages/flutter_driver/lib/src/extension/extension.dart @@ -146,7 +146,7 @@ class FlutterDriverExtension { static final Logger _log = new Logger('FlutterDriverExtension'); - final WidgetController _prober = new LiveWidgetController(WidgetsBinding.instance); + final WidgetController _prober = new WidgetController(WidgetsBinding.instance); final Map _commandHandlers = {}; final Map _commandDeserializers = {}; final Map _finders = {}; diff --git a/packages/flutter_test/lib/src/controller.dart b/packages/flutter_test/lib/src/controller.dart index 9baea2cb9fa..e93bba422c2 100644 --- a/packages/flutter_test/lib/src/controller.dart +++ b/packages/flutter_test/lib/src/controller.dart @@ -15,12 +15,8 @@ import 'test_pointer.dart'; /// Class that programmatically interacts with widgets. /// -/// For a variant of this class suited specifically for unit tests, see -/// [WidgetTester]. For one suitable for live tests on a device, consider -/// [LiveWidgetController]. -/// -/// Concrete subclasses must implement the [pump] method. -abstract class WidgetController { +/// For a variant of this class suited specifically for unit tests, see [WidgetTester]. +class WidgetController { /// Creates a widget controller that uses the given binding. WidgetController(this.binding); @@ -396,11 +392,10 @@ abstract class WidgetController { /// This is invoked by [flingFrom], for instance, so that the sequence of /// pointer events occurs over time. /// - /// The [WidgetTester] subclass implements this by deferring to the [binding]. + /// The default implementation does nothing. /// - /// See also [SchedulerBinding.endOfFrame], which returns a future that could - /// be appropriate to return in the implementation of this method. - Future pump(Duration duration); + /// The [WidgetTester] subclass implements this by deferring to the [binding]. + Future pump(Duration duration) => new Future.value(null); /// Attempts to drag the given widget by the given offset, by /// starting a drag in the middle of the widget. @@ -521,20 +516,3 @@ abstract class WidgetController { /// the widget's render object has been laid out at least once. Rect getRect(Finder finder) => getTopLeft(finder) & getSize(finder); } - -/// Variant of [WidgetController] that can be used in tests running -/// on a device. -/// -/// This is used, for instance, by [FlutterDriver]. -class LiveWidgetController extends WidgetController { - /// Creates a widget controller that uses the given binding. - LiveWidgetController(WidgetsBinding binding) : super(binding); - - @override - Future pump(Duration duration) async { - if (duration != null) - await new Future.delayed(duration); - binding.scheduleFrame(); - await binding.endOfFrame; - } -} diff --git a/packages/flutter_tools/lib/src/android/gradle.dart b/packages/flutter_tools/lib/src/android/gradle.dart index 13162b686b2..8842503497a 100644 --- a/packages/flutter_tools/lib/src/android/gradle.dart +++ b/packages/flutter_tools/lib/src/android/gradle.dart @@ -360,11 +360,11 @@ Future _buildGradleProjectV2( command.add(assembleTask); final int exitCode = await runCommandAndStreamOutput( - command, - workingDirectory: flutterProject.android.directory.path, - allowReentrantFlutter: true, - environment: _gradleEnv, - filter: logger.isVerbose ? null : ndkMessageFilter, + command, + workingDirectory: flutterProject.android.directory.path, + allowReentrantFlutter: true, + environment: _gradleEnv, + filter: logger.isVerbose ? null : ndkMessageFilter, ); status.stop(); diff --git a/packages/flutter_tools/lib/src/base/process.dart b/packages/flutter_tools/lib/src/base/process.dart index 2105aa53da8..6eceade9334 100644 --- a/packages/flutter_tools/lib/src/base/process.dart +++ b/packages/flutter_tools/lib/src/base/process.dart @@ -302,11 +302,10 @@ String runSync(List cmd, { void _traceCommand(List args, { String workingDirectory }) { final String argsText = args.join(' '); - if (workingDirectory == null) { - printTrace('executing: $argsText'); - } else { - printTrace('executing: [$workingDirectory${fs.path.separator}] $argsText'); - } + if (workingDirectory == null) + printTrace(argsText); + else + printTrace('[$workingDirectory${fs.path.separator}] $argsText'); } String _runWithLoggingSync(List cmd, {