
* Update project.pbxproj files to say Flutter rather than Chromium Also, the templates now have an empty organization so that we don't cause people to give their apps a Flutter copyright. * Update the copyright notice checker to require a standard notice on all files * Update copyrights on Dart files. (This was a mechanical commit.) * Fix weird license headers on Dart files that deviate from our conventions; relicense Shrine. Some were already marked "The Flutter Authors", not clear why. Their dates have been normalized. Some were missing the blank line after the license. Some were randomly different in trivial ways for no apparent reason (e.g. missing the trailing period). * Clean up the copyrights in non-Dart files. (Manual edits.) Also, make sure templates don't have copyrights. * Fix some more ORGANIZATIONNAMEs
154 lines
5.8 KiB
Dart
154 lines
5.8 KiB
Dart
// 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 '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/utils.dart';
|
|
|
|
/// 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 {
|
|
final List<String> options = <String>[
|
|
'-v',
|
|
// --release doesn't work on iOS due to code signing issues
|
|
'--profile',
|
|
'-d',
|
|
device.deviceId,
|
|
];
|
|
options.add(benchmarkPath);
|
|
return await _startFlutter(
|
|
options: options,
|
|
canFail: false,
|
|
);
|
|
});
|
|
|
|
return await _readJsonResults(flutterProcess);
|
|
}
|
|
return _run();
|
|
}
|
|
|
|
final Map<String, double> allResults = <String, double>{
|
|
...await _runMicrobench('lib/stocks/layout_bench.dart'),
|
|
...await _runMicrobench('lib/stocks/build_bench.dart'),
|
|
...await _runMicrobench('lib/geometry/matrix_utils_transform_bench.dart'),
|
|
...await _runMicrobench('lib/geometry/rrect_contains_bench.dart'),
|
|
...await _runMicrobench('lib/gestures/velocity_tracker_bench.dart'),
|
|
...await _runMicrobench('lib/gestures/gesture_detector_bench.dart'),
|
|
...await _runMicrobench('lib/stocks/animation_bench.dart'),
|
|
...await _runMicrobench('lib/language/sync_star_bench.dart'),
|
|
...await _runMicrobench('lib/language/sync_star_semantics_bench.dart'),
|
|
};
|
|
|
|
return 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 = flutterCommandArgs('run', 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 ==============';
|
|
const String jsonPrefix = ':::JSON:::';
|
|
bool jsonStarted = false;
|
|
final StringBuffer jsonBuf = StringBuffer();
|
|
final Completer<Map<String, double>> completer = Completer<Map<String, double>>();
|
|
|
|
final StreamSubscription<String> stderrSub = process.stderr
|
|
.transform<String>(const Utf8Decoder())
|
|
.transform<String>(const LineSplitter())
|
|
.listen((String line) {
|
|
stderr.writeln('[STDERR] $line');
|
|
});
|
|
|
|
bool processWasKilledIntentionally = false;
|
|
bool resultsHaveBeenParsed = false;
|
|
final StreamSubscription<String> stdoutSub = process.stdout
|
|
.transform<String>(const Utf8Decoder())
|
|
.transform<String>(const LineSplitter())
|
|
.listen((String line) async {
|
|
print(line);
|
|
|
|
if (line.contains(jsonStart)) {
|
|
jsonStarted = true;
|
|
return;
|
|
}
|
|
|
|
if (line.contains(jsonEnd)) {
|
|
final String jsonOutput = jsonBuf.toString();
|
|
|
|
// If we end up here and have already parsed the results, it suggests that
|
|
// we have received output from another test because our `flutter run`
|
|
// process did not terminate correctly.
|
|
// https://github.com/flutter/flutter/issues/19096#issuecomment-402756549
|
|
if (resultsHaveBeenParsed) {
|
|
throw 'Additional JSON was received after results has already been '
|
|
'processed. This suggests the `flutter run` process may have lived '
|
|
'past the end of our test and collected additional output from the '
|
|
'next test.\n\n'
|
|
'The JSON below contains all collected output, including both from '
|
|
'the original test and what followed.\n\n'
|
|
'$jsonOutput';
|
|
}
|
|
|
|
jsonStarted = false;
|
|
processWasKilledIntentionally = true;
|
|
resultsHaveBeenParsed = true;
|
|
// Sending a SIGINT/SIGTERM to the process here isn't reliable because [process] is
|
|
// the shell (flutter is a shell script) and doesn't pass the signal on.
|
|
// Sending a `q` is an instruction to quit using the console runner.
|
|
// See https://github.com/flutter/flutter/issues/19208
|
|
process.stdin.write('q');
|
|
await process.stdin.flush();
|
|
// Also send a kill signal in case the `q` above didn't work.
|
|
process.kill(ProcessSignal.sigint);
|
|
try {
|
|
completer.complete(Map<String, double>.from(json.decode(jsonOutput)));
|
|
} catch (ex) {
|
|
completer.completeError('Decoding JSON failed ($ex). JSON string was: $jsonOutput');
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (jsonStarted && line.contains(jsonPrefix))
|
|
jsonBuf.writeln(line.substring(line.indexOf(jsonPrefix) + jsonPrefix.length));
|
|
});
|
|
|
|
process.exitCode.then<void>((int code) async {
|
|
await Future.wait<void>(<Future<void>>[
|
|
stdoutSub.cancel(),
|
|
stderrSub.cancel(),
|
|
]);
|
|
if (!processWasKilledIntentionally && code != 0) {
|
|
completer.completeError('flutter run failed: exit code=$code');
|
|
}
|
|
});
|
|
|
|
return completer.future;
|
|
}
|