flutter/dev/devicelab/lib/tasks/microbenchmarks.dart
Ian Hickson 9adb4a78a6 Deep linking: automatically push the route hiearchy on load. (#10894)
The main purpose of this PR is to make it so that when you set the
initial route and it's a hierarchical route (e.g. `/a/b/c`), it
implies multiple pushes, one for each step of the route (so in that
case, `/`, `/a`, `/a/b`, and `/a/b/c`, in that order). If any of those
routes don't exist, it falls back to '/'.

As part of doing that, I:

 * Changed the default for MaterialApp.initialRoute to honor the
   actual initial route.

 * Added a MaterialApp.onUnknownRoute for handling bad routes.

 * Added a feature to flutter_driver that allows the host test script
   and the device test app to communicate.

 * Added a test to make sure `flutter drive --route` works.
   (Hopefully that will also prove `flutter run --route` works, though
   this isn't testing the `flutter` tool's side of that. My main
   concern is over whether the engine side works.)

 * Fixed `flutter drive` to output the right target file name.

 * Changed how the stocks app represents its data, so that we can
   show a page for a stock before we know if it exists.

 * Made it possible to show a stock page that doesn't exist. It shows
   a progress indicator if we're loading the data, or else shows a
   message saying it doesn't exist.

 * Changed the pathing structure of routes in stocks to work more
   sanely.

 * Made search in the stocks app actually work (before it only worked
   if we happened to accidentally trigger a rebuild). Added a test.

 * Replaced some custom code in the stocks app with a BackButton.

 * Added a "color" feature to BackButton to support the stocks use case.

 * Spaced out the ErrorWidget text a bit more.

 * Added `RouteSettings.copyWith`, which I ended up not using.

 * Improved the error messages around routing.

While I was in some files I made a few formatting fixes, fixed some
code health issues, and also removed `flaky: true` from some devicelab
tests that have been stable for a while. Also added some documentation
here and there.
2017-06-23 14:58:29 -07:00

122 lines
4.2 KiB
Dart

// Copyright 2017 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 'dart:convert';
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/ios.dart';
import 'package:flutter_devicelab/framework/utils.dart';
/// The maximum amount of time a single microbenchmarks is allowed to take.
const Duration _kBenchmarkTimeout = const Duration(minutes: 6);
/// Creates a device lab task that runs benchmarks in
/// `dev/benchmarks/microbenchmarks` reports results to the dashboard.
TaskFunction createMicrobenchmarkTask() {
return () async {
final Device device = await devices.workingDevice;
await device.unlock();
Future<Map<String, double>> _runMicrobench(String benchmarkPath) async {
Future<Map<String, double>> _run() async {
print('Running $benchmarkPath');
final Directory appDir = dir(
path.join(flutterDirectory.path, 'dev/benchmarks/microbenchmarks'));
final Process flutterProcess = await inDirectory(appDir, () async {
if (deviceOperatingSystem == DeviceOperatingSystem.ios)
await prepareProvisioningCertificates(appDir.path);
return await _startFlutter(
options: <String>[
'--profile',
// --release doesn't work on iOS due to code signing issues
'-d',
device.deviceId,
benchmarkPath,
],
canFail: false,
);
});
return await _readJsonResults(flutterProcess);
}
return _run().timeout(_kBenchmarkTimeout);
}
final Map<String, double> allResults = <String, double>{};
allResults.addAll(await _runMicrobench('lib/stocks/layout_bench.dart'));
allResults.addAll(await _runMicrobench('lib/stocks/build_bench.dart'));
allResults.addAll(await _runMicrobench('lib/gestures/velocity_tracker_bench.dart'));
allResults.addAll(await _runMicrobench('lib/stocks/animation_bench.dart'));
return new TaskResult.success(allResults, benchmarkScoreKeys: allResults.keys.toList());
};
}
Future<Process> _startFlutter({
String command: 'run',
List<String> options: const <String>[],
bool canFail: false,
Map<String, String> environment,
}) {
final List<String> args = <String>['run']..addAll(options);
return startProcess(path.join(flutterDirectory.path, 'bin', 'flutter'), args, environment: environment);
}
Future<Map<String, double>> _readJsonResults(Process process) {
// IMPORTANT: keep these values in sync with dev/benchmarks/microbenchmarks/lib/common.dart
const String jsonStart = '================ RESULTS ================';
const String jsonEnd = '================ FORMATTED ==============';
bool jsonStarted = false;
final StringBuffer jsonBuf = new StringBuffer();
final Completer<Map<String, double>> completer = new Completer<Map<String, double>>();
final StreamSubscription<String> stderrSub = process.stderr
.transform(const Utf8Decoder())
.transform(const LineSplitter())
.listen((String line) {
stderr.writeln('[STDERR] $line');
});
int prefixLength = 0;
bool processWasKilledIntentionally = false;
final StreamSubscription<String> stdoutSub = process.stdout
.transform(const Utf8Decoder())
.transform(const LineSplitter())
.listen((String line) {
print(line);
if (line.contains(jsonStart)) {
jsonStarted = true;
prefixLength = line.indexOf(jsonStart);
return;
}
if (line.contains(jsonEnd)) {
jsonStarted = false;
processWasKilledIntentionally = true;
process.kill(ProcessSignal.SIGINT); // flutter run doesn't quit automatically
completer.complete(JSON.decode(jsonBuf.toString()));
return;
}
if (jsonStarted)
jsonBuf.writeln(line.substring(prefixLength));
});
process.exitCode.then<int>((int code) {
stdoutSub.cancel();
stderrSub.cancel();
if (!processWasKilledIntentionally && code != 0) {
completer.completeError('flutter run failed: exit code=$code');
}
});
return completer.future;
}