Unverified Commit ef5ffd08 authored by Darren Austin's avatar Darren Austin Committed by GitHub

Migrate devicelab framework code to null safety. (#86325)

(Attempt to reland #85993)
parent 43ed3b6b
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'package:args/command_runner.dart'; import 'package:args/command_runner.dart';
import 'package:flutter_devicelab/framework/runner.dart'; import 'package:flutter_devicelab/framework/runner.dart';
...@@ -64,19 +62,19 @@ class TestCommand extends Command<void> { ...@@ -64,19 +62,19 @@ class TestCommand extends Command<void> {
@override @override
Future<void> run() async { Future<void> run() async {
final List<String> taskArgsRaw = argResults['task-args'] as List<String>; final List<String> taskArgsRaw = argResults!['task-args'] as List<String>;
// Prepend '--' to convert args to options when passed to task // Prepend '--' to convert args to options when passed to task
final List<String> taskArgs = taskArgsRaw.map((String taskArg) => '--$taskArg').toList(); final List<String> taskArgs = taskArgsRaw.map((String taskArg) => '--$taskArg').toList();
print(taskArgs); print(taskArgs);
await runTasks( await runTasks(
<String>[argResults['task'] as String], <String>[argResults!['task'] as String],
deviceId: argResults['device-id'] as String, deviceId: argResults!['device-id'] as String?,
gitBranch: argResults['git-branch'] as String, gitBranch: argResults!['git-branch'] as String?,
localEngine: argResults['local-engine'] as String, localEngine: argResults!['local-engine'] as String?,
localEngineSrcPath: argResults['local-engine-src-path'] as String, localEngineSrcPath: argResults!['local-engine-src-path'] as String?,
luciBuilder: argResults['luci-builder'] as String, luciBuilder: argResults!['luci-builder'] as String?,
resultsPath: argResults['results-file'] as String, resultsPath: argResults!['results-file'] as String?,
silent: argResults['silent'] as bool, silent: (argResults!['silent'] as bool?) ?? false,
taskArgs: taskArgs, taskArgs: taskArgs,
); );
} }
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'package:args/command_runner.dart'; import 'package:args/command_runner.dart';
import '../framework/cocoon.dart'; import '../framework/cocoon.dart';
...@@ -25,8 +23,8 @@ class UploadMetricsCommand extends Command<void> { ...@@ -25,8 +23,8 @@ class UploadMetricsCommand extends Command<void> {
@override @override
Future<void> run() async { Future<void> run() async {
final String resultsPath = argResults['results-file'] as String; final String resultsPath = argResults!['results-file'] as String;
final String serviceAccountTokenFile = argResults['service-account-token-file'] as String; final String? serviceAccountTokenFile = argResults!['service-account-token-file'] as String?;
final Cocoon cocoon = Cocoon(serviceAccountTokenPath: serviceAccountTokenFile); final Cocoon cocoon = Cocoon(serviceAccountTokenPath: serviceAccountTokenFile);
return cocoon.sendResultsPath(resultsPath); return cocoon.sendResultsPath(resultsPath);
......
...@@ -2,10 +2,7 @@ ...@@ -2,10 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:math' as math; import 'dart:math' as math;
import 'package:meta/meta.dart';
import 'task_result.dart'; import 'task_result.dart';
...@@ -43,8 +40,8 @@ class ABTest { ...@@ -43,8 +40,8 @@ class ABTest {
final String localEngine; final String localEngine;
final String taskName; final String taskName;
final DateTime runStart; final DateTime runStart;
DateTime _runEnd; DateTime? _runEnd;
DateTime get runEnd => _runEnd; DateTime? get runEnd => _runEnd;
final Map<String, List<double>> _aResults; final Map<String, List<double>> _aResults;
final Map<String, List<double>> _bResults; final Map<String, List<double>> _bResults;
...@@ -91,15 +88,15 @@ class ABTest { ...@@ -91,15 +88,15 @@ class ABTest {
kLocalEngineKeyName: localEngine, kLocalEngineKeyName: localEngine,
kTaskNameKeyName: taskName, kTaskNameKeyName: taskName,
kRunStartKeyName: runStart.toIso8601String(), kRunStartKeyName: runStart.toIso8601String(),
kRunEndKeyName: runEnd.toIso8601String(), kRunEndKeyName: runEnd!.toIso8601String(),
kAResultsKeyName: _aResults, kAResultsKeyName: _aResults,
kBResultsKeyName: _bResults, kBResultsKeyName: _bResults,
}; };
static void updateColumnLengths(List<int> lengths, List<String> results) { static void updateColumnLengths(List<int> lengths, List<String?> results) {
for (int column = 0; column < lengths.length; column++) { for (int column = 0; column < lengths.length; column++) {
if (results[column] != null) { if (results[column] != null) {
lengths[column] = math.max(lengths[column], results[column].length); lengths[column] = math.max(lengths[column], results[column]?.length ?? 0);
} }
} }
} }
...@@ -107,10 +104,10 @@ class ABTest { ...@@ -107,10 +104,10 @@ class ABTest {
static void formatResult(StringBuffer buffer, static void formatResult(StringBuffer buffer,
List<int> lengths, List<int> lengths,
List<FieldJustification> aligns, List<FieldJustification> aligns,
List<String> values) { List<String?> values) {
for (int column = 0; column < lengths.length; column++) { for (int column = 0; column < lengths.length; column++) {
final int len = lengths[column]; final int len = lengths[column];
String value = values[column]; String? value = values[column];
if (value == null) { if (value == null) {
value = ''.padRight(len); value = ''.padRight(len);
} else { } else {
...@@ -142,9 +139,9 @@ class ABTest { ...@@ -142,9 +139,9 @@ class ABTest {
final Map<String, _ScoreSummary> summariesA = _summarize(_aResults); final Map<String, _ScoreSummary> summariesA = _summarize(_aResults);
final Map<String, _ScoreSummary> summariesB = _summarize(_bResults); final Map<String, _ScoreSummary> summariesB = _summarize(_bResults);
final List<List<String>> tableRows = <List<String>>[ final List<List<String?>> tableRows = <List<String?>>[
for (final String scoreKey in <String>{...summariesA.keys, ...summariesB.keys}) for (final String scoreKey in <String>{...summariesA.keys, ...summariesB.keys})
<String>[ <String?>[
scoreKey, scoreKey,
summariesA[scoreKey]?.averageString, summariesA[scoreKey]?.noiseString, summariesA[scoreKey]?.averageString, summariesA[scoreKey]?.noiseString,
summariesB[scoreKey]?.averageString, summariesB[scoreKey]?.noiseString, summariesB[scoreKey]?.averageString, summariesB[scoreKey]?.noiseString,
...@@ -167,7 +164,7 @@ class ABTest { ...@@ -167,7 +164,7 @@ class ABTest {
final List<int> lengths = List<int>.filled(6, 0); final List<int> lengths = List<int>.filled(6, 0);
updateColumnLengths(lengths, titles); updateColumnLengths(lengths, titles);
for (final List<String> row in tableRows) { for (final List<String?> row in tableRows) {
updateColumnLengths(lengths, row); updateColumnLengths(lengths, row);
} }
...@@ -177,7 +174,7 @@ class ABTest { ...@@ -177,7 +174,7 @@ class ABTest {
FieldJustification.CENTER, FieldJustification.CENTER,
...alignments.skip(1), ...alignments.skip(1),
], titles); ], titles);
for (final List<String> row in tableRows) { for (final List<String?> row in tableRows) {
formatResult(buffer, lengths, alignments, row); formatResult(buffer, lengths, alignments, row);
} }
...@@ -192,7 +189,7 @@ class ABTest { ...@@ -192,7 +189,7 @@ class ABTest {
buffer.writeln('$scoreKey:'); buffer.writeln('$scoreKey:');
buffer.write(' A:\t'); buffer.write(' A:\t');
if (_aResults.containsKey(scoreKey)) { if (_aResults.containsKey(scoreKey)) {
for (final double score in _aResults[scoreKey]) { for (final double score in _aResults[scoreKey]!) {
buffer.write('${score.toStringAsFixed(2)}\t'); buffer.write('${score.toStringAsFixed(2)}\t');
} }
} else { } else {
...@@ -202,7 +199,7 @@ class ABTest { ...@@ -202,7 +199,7 @@ class ABTest {
buffer.write(' B:\t'); buffer.write(' B:\t');
if (_bResults.containsKey(scoreKey)) { if (_bResults.containsKey(scoreKey)) {
for (final double score in _bResults[scoreKey]) { for (final double score in _bResults[scoreKey]!) {
buffer.write('${score.toStringAsFixed(2)}\t'); buffer.write('${score.toStringAsFixed(2)}\t');
} }
} else { } else {
...@@ -232,8 +229,8 @@ class ABTest { ...@@ -232,8 +229,8 @@ class ABTest {
); );
for (final String scoreKey in _allScoreKeys) { for (final String scoreKey in _allScoreKeys) {
final _ScoreSummary summaryA = summariesA[scoreKey]; final _ScoreSummary? summaryA = summariesA[scoreKey];
final _ScoreSummary summaryB = summariesB[scoreKey]; final _ScoreSummary? summaryB = summariesB[scoreKey];
buffer.write('$scoreKey\t'); buffer.write('$scoreKey\t');
if (summaryA != null) { if (summaryA != null) {
...@@ -261,8 +258,8 @@ class ABTest { ...@@ -261,8 +258,8 @@ class ABTest {
class _ScoreSummary { class _ScoreSummary {
_ScoreSummary({ _ScoreSummary({
@required this.average, required this.average,
@required this.noise, required this.noise,
}); });
/// Average (arithmetic mean) of a series of values collected by a benchmark. /// Average (arithmetic mean) of a series of values collected by a benchmark.
...@@ -275,14 +272,14 @@ class _ScoreSummary { ...@@ -275,14 +272,14 @@ class _ScoreSummary {
String get averageString => average.toStringAsFixed(2); String get averageString => average.toStringAsFixed(2);
String get noiseString => '(${_ratioToPercent(noise)})'; String get noiseString => '(${_ratioToPercent(noise)})';
String improvementOver(_ScoreSummary other) { String improvementOver(_ScoreSummary? other) {
return other == null ? '' : '${(average / other.average).toStringAsFixed(2)}x'; return other == null ? '' : '${(average / other.average).toStringAsFixed(2)}x';
} }
} }
void _addResult(TaskResult result, Map<String, List<double>> results) { void _addResult(TaskResult result, Map<String, List<double>> results) {
for (final String scoreKey in result.benchmarkScoreKeys) { for (final String scoreKey in result.benchmarkScoreKeys ?? <String>[]) {
final double score = (result.data[scoreKey] as num).toDouble(); final double score = (result.data![scoreKey] as num).toDouble();
results.putIfAbsent(scoreKey, () => <double>[]).add(score); results.putIfAbsent(scoreKey, () => <double>[]).add(score);
} }
} }
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:io'; import 'dart:io';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
...@@ -106,7 +104,7 @@ bool hasMultipleOccurrences(String text, Pattern pattern) { ...@@ -106,7 +104,7 @@ bool hasMultipleOccurrences(String text, Pattern pattern) {
/// The Android home directory. /// The Android home directory.
String get _androidHome { String get _androidHome {
final String androidHome = Platform.environment['ANDROID_HOME'] ?? final String? androidHome = Platform.environment['ANDROID_HOME'] ??
Platform.environment['ANDROID_SDK_ROOT']; Platform.environment['ANDROID_SDK_ROOT'];
if (androidHome == null || androidHome.isEmpty) { if (androidHome == null || androidHome.isEmpty) {
throw Exception('Environment variable `ANDROID_SDK_ROOT` is not set.'); throw Exception('Environment variable `ANDROID_SDK_ROOT` is not set.');
...@@ -118,9 +116,9 @@ String get _androidHome { ...@@ -118,9 +116,9 @@ String get _androidHome {
Future<String> _evalApkAnalyzer( Future<String> _evalApkAnalyzer(
List<String> args, { List<String> args, {
bool printStdout = false, bool printStdout = false,
String workingDirectory, String? workingDirectory,
}) async { }) async {
final String javaHome = await findJavaHome(); final String? javaHome = await findJavaHome();
if (javaHome == null || javaHome.isEmpty) { if (javaHome == null || javaHome.isEmpty) {
throw Exception('No JAVA_HOME set.'); throw Exception('No JAVA_HOME set.');
} }
...@@ -259,7 +257,7 @@ class FlutterProject { ...@@ -259,7 +257,7 @@ class FlutterProject {
String get androidPath => path.join(rootPath, 'android'); String get androidPath => path.join(rootPath, 'android');
String get iosPath => path.join(rootPath, 'ios'); String get iosPath => path.join(rootPath, 'ios');
Future<void> addCustomBuildType(String name, {String initWith}) async { Future<void> addCustomBuildType(String name, {required String initWith}) async {
final File buildScript = File( final File buildScript = File(
path.join(androidPath, 'app', 'build.gradle'), path.join(androidPath, 'app', 'build.gradle'),
); );
...@@ -276,7 +274,7 @@ android { ...@@ -276,7 +274,7 @@ android {
'''); ''');
} }
Future<void> addGlobalBuildType(String name, {String initWith}) async { Future<void> addGlobalBuildType(String name, {required String initWith}) async {
final File buildScript = File( final File buildScript = File(
path.join(androidPath, 'build.gradle'), path.join(androidPath, 'build.gradle'),
); );
...@@ -360,11 +358,11 @@ flutter: ...@@ -360,11 +358,11 @@ flutter:
pubspec.writeAsStringSync(newContents); pubspec.writeAsStringSync(newContents);
} }
Future<void> runGradleTask(String task, {List<String> options}) async { Future<void> runGradleTask(String task, {List<String>? options}) async {
return _runGradleTask(workingDirectory: androidPath, task: task, options: options); return _runGradleTask(workingDirectory: androidPath, task: task, options: options);
} }
Future<ProcessResult> resultOfGradleTask(String task, {List<String> options}) { Future<ProcessResult> resultOfGradleTask(String task, {List<String>? options}) {
return _resultOfGradleTask(workingDirectory: androidPath, task: task, options: options); return _resultOfGradleTask(workingDirectory: androidPath, task: task, options: options);
} }
...@@ -416,7 +414,11 @@ class FlutterModuleProject { ...@@ -416,7 +414,11 @@ class FlutterModuleProject {
String get rootPath => path.join(parent.path, name); String get rootPath => path.join(parent.path, name);
} }
Future<void> _runGradleTask({String workingDirectory, String task, List<String> options}) async { Future<void> _runGradleTask({
required String workingDirectory,
required String task,
List<String>? options,
}) async {
final ProcessResult result = await _resultOfGradleTask( final ProcessResult result = await _resultOfGradleTask(
workingDirectory: workingDirectory, workingDirectory: workingDirectory,
task: task, task: task,
...@@ -431,10 +433,13 @@ Future<void> _runGradleTask({String workingDirectory, String task, List<String> ...@@ -431,10 +433,13 @@ Future<void> _runGradleTask({String workingDirectory, String task, List<String>
throw 'Gradle exited with error'; throw 'Gradle exited with error';
} }
Future<ProcessResult> _resultOfGradleTask({String workingDirectory, String task, Future<ProcessResult> _resultOfGradleTask({
List<String> options}) async { required String workingDirectory,
required String task,
List<String>? options,
}) async {
section('Find Java'); section('Find Java');
final String javaHome = await findJavaHome(); final String? javaHome = await findJavaHome();
if (javaHome == null) if (javaHome == null)
throw TaskResult.failure('Could not find Java'); throw TaskResult.failure('Could not find Java');
...@@ -465,7 +470,7 @@ Future<ProcessResult> _resultOfGradleTask({String workingDirectory, String task, ...@@ -465,7 +470,7 @@ Future<ProcessResult> _resultOfGradleTask({String workingDirectory, String task,
} }
/// Returns [null] if target matches [expectedTarget], otherwise returns an error message. /// Returns [null] if target matches [expectedTarget], otherwise returns an error message.
String validateSnapshotDependency(FlutterProject project, String expectedTarget) { String? validateSnapshotDependency(FlutterProject project, String expectedTarget) {
final File snapshotBlob = File( final File snapshotBlob = File(
path.join(project.rootPath, 'build', 'app', 'intermediates', path.join(project.rootPath, 'build', 'app', 'intermediates',
'flutter', 'debug', 'flutter_build.d')); 'flutter', 'debug', 'flutter_build.d'));
......
...@@ -2,15 +2,12 @@ ...@@ -2,15 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:async'; import 'dart:async';
import 'dart:convert' show json, utf8, LineSplitter, JsonEncoder; import 'dart:convert' show json, utf8, LineSplitter, JsonEncoder;
import 'dart:io' as io; import 'dart:io' as io;
import 'dart:math' as math; import 'dart:math' as math;
import 'package:flutter_devicelab/common.dart'; import 'package:flutter_devicelab/common.dart';
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart'; import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
...@@ -32,10 +29,10 @@ class ChromeOptions { ...@@ -32,10 +29,10 @@ class ChromeOptions {
}); });
/// If not null passed as `--user-data-dir`. /// If not null passed as `--user-data-dir`.
final String userDataDirectory; final String? userDataDirectory;
/// If not null launches a Chrome tab at this URL. /// If not null launches a Chrome tab at this URL.
final String url; final String? url;
/// The width of the Chrome window. /// The width of the Chrome window.
/// ///
...@@ -49,14 +46,14 @@ class ChromeOptions { ...@@ -49,14 +46,14 @@ class ChromeOptions {
/// Launches code in "headless" mode, which allows running Chrome in /// Launches code in "headless" mode, which allows running Chrome in
/// environments without a display, such as LUCI and Cirrus. /// environments without a display, such as LUCI and Cirrus.
final bool headless; final bool? headless;
/// The port Chrome will use for its debugging protocol. /// The port Chrome will use for its debugging protocol.
/// ///
/// If null, Chrome is launched without debugging. When running in headless /// If null, Chrome is launched without debugging. When running in headless
/// mode without a debug port, Chrome quits immediately. For most tests it is /// mode without a debug port, Chrome quits immediately. For most tests it is
/// typical to set [headless] to true and set a non-null debug port. /// typical to set [headless] to true and set a non-null debug port.
final int debugPort; final int? debugPort;
} }
/// A function called when the Chrome process encounters an error. /// A function called when the Chrome process encounters an error.
...@@ -79,7 +76,7 @@ class Chrome { ...@@ -79,7 +76,7 @@ class Chrome {
/// The [onError] callback is called with an error message when the Chrome /// The [onError] callback is called with an error message when the Chrome
/// process encounters an error. In particular, [onError] is called when the /// process encounters an error. In particular, [onError] is called when the
/// Chrome process exits prematurely, i.e. before [stop] is called. /// Chrome process exits prematurely, i.e. before [stop] is called.
static Future<Chrome> launch(ChromeOptions options, { String workingDirectory, @required ChromeErrorCallback onError }) async { static Future<Chrome> launch(ChromeOptions options, { String? workingDirectory, required ChromeErrorCallback onError }) async {
if (!io.Platform.isWindows) { if (!io.Platform.isWindows) {
final io.ProcessResult versionResult = io.Process.runSync(_findSystemChromeExecutable(), const <String>['--version']); final io.ProcessResult versionResult = io.Process.runSync(_findSystemChromeExecutable(), const <String>['--version']);
print('Launching ${versionResult.stdout}'); print('Launching ${versionResult.stdout}');
...@@ -92,10 +89,10 @@ class Chrome { ...@@ -92,10 +89,10 @@ class Chrome {
if (options.userDataDirectory != null) if (options.userDataDirectory != null)
'--user-data-dir=${options.userDataDirectory}', '--user-data-dir=${options.userDataDirectory}',
if (options.url != null) if (options.url != null)
options.url, options.url!,
if (io.Platform.environment['CHROME_NO_SANDBOX'] == 'true') if (io.Platform.environment['CHROME_NO_SANDBOX'] == 'true')
'--no-sandbox', '--no-sandbox',
if (options.headless) if (options.headless == true)
'--headless', '--headless',
if (withDebugging) if (withDebugging)
'--remote-debugging-port=${options.debugPort}', '--remote-debugging-port=${options.debugPort}',
...@@ -116,9 +113,9 @@ class Chrome { ...@@ -116,9 +113,9 @@ class Chrome {
workingDirectory: workingDirectory, workingDirectory: workingDirectory,
); );
WipConnection debugConnection; WipConnection? debugConnection;
if (withDebugging) { if (withDebugging) {
debugConnection = await _connectToChromeDebugPort(chromeProcess, options.debugPort); debugConnection = await _connectToChromeDebugPort(chromeProcess, options.debugPort!);
} }
return Chrome._(chromeProcess, onError, debugConnection); return Chrome._(chromeProcess, onError, debugConnection);
...@@ -126,12 +123,12 @@ class Chrome { ...@@ -126,12 +123,12 @@ class Chrome {
final io.Process _chromeProcess; final io.Process _chromeProcess;
final ChromeErrorCallback _onError; final ChromeErrorCallback _onError;
final WipConnection _debugConnection; final WipConnection? _debugConnection;
bool _isStopped = false; bool _isStopped = false;
Completer<void> _tracingCompleter; Completer<void> ?_tracingCompleter;
StreamSubscription<WipEvent> _tracingSubscription; StreamSubscription<WipEvent>? _tracingSubscription;
List<Map<String, dynamic>> _tracingData; List<Map<String, dynamic>>? _tracingData;
/// Starts recording a performance trace. /// Starts recording a performance trace.
/// ///
...@@ -151,24 +148,24 @@ class Chrome { ...@@ -151,24 +148,24 @@ class Chrome {
// Subscribe to tracing events prior to calling "Tracing.start". Otherwise, // Subscribe to tracing events prior to calling "Tracing.start". Otherwise,
// we'll miss tracing data. // we'll miss tracing data.
_tracingSubscription = _debugConnection.onNotification.listen((WipEvent event) { _tracingSubscription = _debugConnection?.onNotification.listen((WipEvent event) {
// We receive data as a sequence of "Tracing.dataCollected" followed by // We receive data as a sequence of "Tracing.dataCollected" followed by
// "Tracing.tracingComplete" at the end. Until "Tracing.tracingComplete" // "Tracing.tracingComplete" at the end. Until "Tracing.tracingComplete"
// is received, the data may be incomplete. // is received, the data may be incomplete.
if (event.method == 'Tracing.tracingComplete') { if (event.method == 'Tracing.tracingComplete') {
_tracingCompleter.complete(); _tracingCompleter!.complete();
_tracingSubscription.cancel(); _tracingSubscription!.cancel();
_tracingSubscription = null; _tracingSubscription = null;
} else if (event.method == 'Tracing.dataCollected') { } else if (event.method == 'Tracing.dataCollected') {
final dynamic value = event.params['value']; final dynamic value = event.params?['value'];
if (value is! List) { if (value is! List) {
throw FormatException('"Tracing.dataCollected" returned malformed data. ' throw FormatException('"Tracing.dataCollected" returned malformed data. '
'Expected a List but got: ${value.runtimeType}'); 'Expected a List but got: ${value.runtimeType}');
} }
_tracingData.addAll((event.params['value'] as List<dynamic>).cast<Map<String, dynamic>>()); _tracingData?.addAll((event.params?['value'] as List<dynamic>).cast<Map<String, dynamic>>());
} }
}); });
await _debugConnection.sendCommand('Tracing.start', <String, dynamic>{ await _debugConnection?.sendCommand('Tracing.start', <String, dynamic>{
// The choice of categories is as follows: // The choice of categories is as follows:
// //
// blink: // blink:
...@@ -190,22 +187,23 @@ class Chrome { ...@@ -190,22 +187,23 @@ class Chrome {
/// Stops a performance tracing session started by [beginRecordingPerformance]. /// Stops a performance tracing session started by [beginRecordingPerformance].
/// ///
/// Returns all the collected tracing data unfiltered. /// Returns all the collected tracing data unfiltered.
Future<List<Map<String, dynamic>>> endRecordingPerformance() async { Future<List<Map<String, dynamic>>?> endRecordingPerformance() async {
await _debugConnection.sendCommand('Tracing.end'); await _debugConnection!.sendCommand('Tracing.end');
await _tracingCompleter.future; await _tracingCompleter!.future;
final List<Map<String, dynamic>> data = _tracingData; final List<Map<String, dynamic>>? data = _tracingData;
_tracingCompleter = null; _tracingCompleter = null;
_tracingData = null; _tracingData = null;
return data; return data;
} }
Future<void> reloadPage({bool ignoreCache = false}) async { Future<void> reloadPage({bool ignoreCache = false}) async {
await _debugConnection.page.reload(ignoreCache: ignoreCache); await _debugConnection?.page.reload(ignoreCache: ignoreCache);
} }
/// Stops the Chrome process. /// Stops the Chrome process.
void stop() { void stop() {
_isStopped = true; _isStopped = true;
_tracingSubscription?.cancel();
_chromeProcess.kill(); _chromeProcess.kill();
} }
} }
...@@ -214,7 +212,7 @@ String _findSystemChromeExecutable() { ...@@ -214,7 +212,7 @@ String _findSystemChromeExecutable() {
// On some environments, such as the Dart HHH tester, Chrome resides in a // On some environments, such as the Dart HHH tester, Chrome resides in a
// non-standard location and is provided via the following environment // non-standard location and is provided via the following environment
// variable. // variable.
final String envExecutable = io.Platform.environment['CHROME_EXECUTABLE']; final String? envExecutable = io.Platform.environment['CHROME_EXECUTABLE'];
if (envExecutable != null) { if (envExecutable != null) {
return envExecutable; return envExecutable;
} }
...@@ -232,15 +230,12 @@ String _findSystemChromeExecutable() { ...@@ -232,15 +230,12 @@ String _findSystemChromeExecutable() {
return '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'; return '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
} else if (io.Platform.isWindows) { } else if (io.Platform.isWindows) {
const String kWindowsExecutable = r'Google\Chrome\Application\chrome.exe'; const String kWindowsExecutable = r'Google\Chrome\Application\chrome.exe';
final List<String> kWindowsPrefixes = <String>[ final List<String> kWindowsPrefixes = <String?>[
io.Platform.environment['LOCALAPPDATA'], io.Platform.environment['LOCALAPPDATA'],
io.Platform.environment['PROGRAMFILES'], io.Platform.environment['PROGRAMFILES'],
io.Platform.environment['PROGRAMFILES(X86)'], io.Platform.environment['PROGRAMFILES(X86)'],
]; ].whereType<String>().toList();
final String windowsPrefix = kWindowsPrefixes.firstWhere((String prefix) { final String windowsPrefix = kWindowsPrefixes.firstWhere((String prefix) {
if (prefix == null) {
return false;
}
final String expectedPath = path.join(prefix, kWindowsExecutable); final String expectedPath = path.join(prefix, kWindowsExecutable);
return io.File(expectedPath).existsSync(); return io.File(expectedPath).existsSync();
}, orElse: () => '.'); }, orElse: () => '.');
...@@ -269,7 +264,7 @@ Future<Uri> _getRemoteDebuggerUrl(Uri base) async { ...@@ -269,7 +264,7 @@ Future<Uri> _getRemoteDebuggerUrl(Uri base) async {
final io.HttpClient client = io.HttpClient(); final io.HttpClient client = io.HttpClient();
final io.HttpClientRequest request = await client.getUrl(base.resolve('/json/list')); final io.HttpClientRequest request = await client.getUrl(base.resolve('/json/list'));
final io.HttpClientResponse response = await request.close(); final io.HttpClientResponse response = await request.close();
final List<dynamic> jsonObject = await json.fuse(utf8).decoder.bind(response).single as List<dynamic>; final List<dynamic>? jsonObject = await json.fuse(utf8).decoder.bind(response).single as List<dynamic>?;
if (jsonObject == null || jsonObject.isEmpty) { if (jsonObject == null || jsonObject.isEmpty) {
return base; return base;
} }
...@@ -279,17 +274,17 @@ Future<Uri> _getRemoteDebuggerUrl(Uri base) async { ...@@ -279,17 +274,17 @@ Future<Uri> _getRemoteDebuggerUrl(Uri base) async {
/// Summarizes a Blink trace down to a few interesting values. /// Summarizes a Blink trace down to a few interesting values.
class BlinkTraceSummary { class BlinkTraceSummary {
BlinkTraceSummary._({ BlinkTraceSummary._({
@required this.averageBeginFrameTime, required this.averageBeginFrameTime,
@required this.averageUpdateLifecyclePhasesTime, required this.averageUpdateLifecyclePhasesTime,
}) : averageTotalUIFrameTime = averageBeginFrameTime + averageUpdateLifecyclePhasesTime; }) : averageTotalUIFrameTime = averageBeginFrameTime + averageUpdateLifecyclePhasesTime;
static BlinkTraceSummary fromJson(List<Map<String, dynamic>> traceJson) { static BlinkTraceSummary? fromJson(List<Map<String, dynamic>> traceJson) {
try { try {
// Convert raw JSON data to BlinkTraceEvent objects sorted by timestamp. // Convert raw JSON data to BlinkTraceEvent objects sorted by timestamp.
List<BlinkTraceEvent> events = traceJson List<BlinkTraceEvent> events = traceJson
.map<BlinkTraceEvent>(BlinkTraceEvent.fromJson) .map<BlinkTraceEvent>(BlinkTraceEvent.fromJson)
.toList() .toList()
..sort((BlinkTraceEvent a, BlinkTraceEvent b) => a.ts - b.ts); ..sort((BlinkTraceEvent a, BlinkTraceEvent b) => a.ts! - b.ts!);
Exception noMeasuredFramesFound() => Exception( Exception noMeasuredFramesFound() => Exception(
'No measured frames found in benchmark tracing data. This likely ' 'No measured frames found in benchmark tracing data. This likely '
...@@ -316,7 +311,7 @@ class BlinkTraceSummary { ...@@ -316,7 +311,7 @@ class BlinkTraceSummary {
return null; return null;
} }
final int tabPid = firstMeasuredFrameEvent.pid; final int tabPid = firstMeasuredFrameEvent.pid!;
// Filter out data from unrelated processes // Filter out data from unrelated processes
events = events.where((BlinkTraceEvent element) => element.pid == tabPid).toList(); events = events.where((BlinkTraceEvent element) => element.pid == tabPid).toList();
...@@ -352,8 +347,8 @@ class BlinkTraceSummary { ...@@ -352,8 +347,8 @@ class BlinkTraceSummary {
// Compute averages and summarize. // Compute averages and summarize.
return BlinkTraceSummary._( return BlinkTraceSummary._(
averageBeginFrameTime: _computeAverageDuration(frames.map((BlinkFrame frame) => frame.beginFrame).toList()), averageBeginFrameTime: _computeAverageDuration(frames.map((BlinkFrame frame) => frame.beginFrame).whereType<BlinkTraceEvent>().toList()),
averageUpdateLifecyclePhasesTime: _computeAverageDuration(frames.map((BlinkFrame frame) => frame.updateAllLifecyclePhases).toList()), averageUpdateLifecyclePhasesTime: _computeAverageDuration(frames.map((BlinkFrame frame) => frame.updateAllLifecyclePhases).whereType<BlinkTraceEvent>().toList()),
); );
} catch (_, __) { } catch (_, __) {
final io.File traceFile = io.File('./chrome-trace.json'); final io.File traceFile = io.File('./chrome-trace.json');
...@@ -392,16 +387,16 @@ class BlinkTraceSummary { ...@@ -392,16 +387,16 @@ class BlinkTraceSummary {
/// Contains events pertaining to a single frame in the Blink trace data. /// Contains events pertaining to a single frame in the Blink trace data.
class BlinkFrame { class BlinkFrame {
/// Corresponds to 'WebViewImpl::beginFrame' event. /// Corresponds to 'WebViewImpl::beginFrame' event.
BlinkTraceEvent beginFrame; BlinkTraceEvent? beginFrame;
/// Corresponds to 'WebViewImpl::updateAllLifecyclePhases' event. /// Corresponds to 'WebViewImpl::updateAllLifecyclePhases' event.
BlinkTraceEvent updateAllLifecyclePhases; BlinkTraceEvent? updateAllLifecyclePhases;
/// Corresponds to 'measured_frame' begin event. /// Corresponds to 'measured_frame' begin event.
BlinkTraceEvent beginMeasuredFrame; BlinkTraceEvent? beginMeasuredFrame;
/// Corresponds to 'measured_frame' end event. /// Corresponds to 'measured_frame' end event.
BlinkTraceEvent endMeasuredFrame; BlinkTraceEvent? endMeasuredFrame;
} }
/// Takes a list of events that have non-null [BlinkTraceEvent.tdur] computes /// Takes a list of events that have non-null [BlinkTraceEvent.tdur] computes
...@@ -414,7 +409,7 @@ Duration _computeAverageDuration(List<BlinkTraceEvent> events) { ...@@ -414,7 +409,7 @@ Duration _computeAverageDuration(List<BlinkTraceEvent> events) {
if (event.tdur == null) { if (event.tdur == null) {
throw FormatException('Trace event lacks "tdur" field: $event'); throw FormatException('Trace event lacks "tdur" field: $event');
} }
return previousValue + event.tdur; return previousValue + event.tdur!;
}); });
final int sampleCount = math.min(events.length, _kMeasuredSampleCount); final int sampleCount = math.min(events.length, _kMeasuredSampleCount);
return Duration(microseconds: sum ~/ sampleCount); return Duration(microseconds: sum ~/ sampleCount);
...@@ -426,15 +421,15 @@ Duration _computeAverageDuration(List<BlinkTraceEvent> events) { ...@@ -426,15 +421,15 @@ Duration _computeAverageDuration(List<BlinkTraceEvent> events) {
/// * https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview /// * https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview
class BlinkTraceEvent { class BlinkTraceEvent {
BlinkTraceEvent._({ BlinkTraceEvent._({
@required this.args, required this.args,
@required this.cat, required this.cat,
@required this.name, required this.name,
@required this.ph, required this.ph,
@required this.pid, this.pid,
@required this.tid, this.tid,
@required this.ts, this.ts,
@required this.tts, this.tts,
@required this.tdur, this.tdur,
}); });
/// Parses an event from its JSON representation. /// Parses an event from its JSON representation.
...@@ -488,19 +483,19 @@ class BlinkTraceEvent { ...@@ -488,19 +483,19 @@ class BlinkTraceEvent {
final String ph; final String ph;
/// Process ID of the process that emitted the event. /// Process ID of the process that emitted the event.
final int pid; final int? pid;
/// Thread ID of the thread that emitted the event. /// Thread ID of the thread that emitted the event.
final int tid; final int? tid;
/// Timestamp in microseconds using tracer clock. /// Timestamp in microseconds using tracer clock.
final int ts; final int? ts;
/// Timestamp in microseconds using thread clock. /// Timestamp in microseconds using thread clock.
final int tts; final int? tts;
/// Event duration in microseconds. /// Event duration in microseconds.
final int tdur; final int? tdur;
/// A "begin frame" event contains all of the scripting time of an animation /// A "begin frame" event contains all of the scripting time of an animation
/// frame (JavaScript, WebAssembly), plus a negligible amount of internal /// frame (JavaScript, WebAssembly), plus a negligible amount of internal
...@@ -556,8 +551,8 @@ class BlinkTraceEvent { ...@@ -556,8 +551,8 @@ class BlinkTraceEvent {
/// validation and conversion is needed. /// validation and conversion is needed.
/// ///
/// Returns null if the value is null. /// Returns null if the value is null.
int _readInt(Map<String, dynamic> json, String key) { int? _readInt(Map<String, dynamic> json, String key) {
final num jsonValue = json[key] as num; final num? jsonValue = json[key] as num?;
if (jsonValue == null) { if (jsonValue == null) {
return null; return null;
...@@ -578,10 +573,10 @@ int _readInt(Map<String, dynamic> json, String key) { ...@@ -578,10 +573,10 @@ int _readInt(Map<String, dynamic> json, String key) {
/// Inconsistency detected by ld.so: ../elf/dl-tls.c: 493: _dl_allocate_tls_init: Assertion `listp->slotinfo[cnt].gen <= GL(dl_tls_generation)' failed! /// Inconsistency detected by ld.so: ../elf/dl-tls.c: 493: _dl_allocate_tls_init: Assertion `listp->slotinfo[cnt].gen <= GL(dl_tls_generation)' failed!
const String _kGlibcError = 'Inconsistency detected by ld.so'; const String _kGlibcError = 'Inconsistency detected by ld.so';
Future<io.Process> _spawnChromiumProcess(String executable, List<String> args, { String workingDirectory }) async { Future<io.Process> _spawnChromiumProcess(String executable, List<String> args, { String? workingDirectory }) async {
// Keep attempting to launch the browser until one of: // Keep attempting to launch the browser until one of:
// - Chrome launched successfully, in which case we just return from the loop. // - Chrome launched successfully, in which case we just return from the loop.
// - The tool detected an unretriable Chrome error, in which case we throw ToolExit. // - The tool detected an unretryable Chrome error, in which case we throw ToolExit.
while (true) { while (true) {
final io.Process process = await io.Process.start(executable, args, workingDirectory: workingDirectory); final io.Process process = await io.Process.start(executable, args, workingDirectory: workingDirectory);
...@@ -611,7 +606,7 @@ Future<io.Process> _spawnChromiumProcess(String executable, List<String> args, { ...@@ -611,7 +606,7 @@ Future<io.Process> _spawnChromiumProcess(String executable, List<String> args, {
'Encountered glibc bug https://sourceware.org/bugzilla/show_bug.cgi?id=19329. ' 'Encountered glibc bug https://sourceware.org/bugzilla/show_bug.cgi?id=19329. '
'Will try launching browser again.', 'Will try launching browser again.',
); );
return null; return '';
} }
print('Failed to launch browser. Command used to launch it: ${args.join(' ')}'); print('Failed to launch browser. Command used to launch it: ${args.join(' ')}');
throw Exception( throw Exception(
...@@ -630,7 +625,7 @@ Future<io.Process> _spawnChromiumProcess(String executable, List<String> args, { ...@@ -630,7 +625,7 @@ Future<io.Process> _spawnChromiumProcess(String executable, List<String> args, {
// launching more processes. // launching more processes.
unawaited(process.exitCode.timeout(const Duration(seconds: 1), onTimeout: () { unawaited(process.exitCode.timeout(const Duration(seconds: 1), onTimeout: () {
process.kill(); process.kill();
return null; return 0;
})); }));
} }
} }
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:async'; import 'dart:async';
import 'dart:convert' show Encoding, json; import 'dart:convert' show Encoding, json;
import 'dart:io'; import 'dart:io';
...@@ -20,12 +18,12 @@ import 'utils.dart'; ...@@ -20,12 +18,12 @@ import 'utils.dart';
typedef ProcessRunSync = ProcessResult Function( typedef ProcessRunSync = ProcessResult Function(
String, String,
List<String>, { List<String>, {
Map<String, String> environment, Map<String, String>? environment,
bool includeParentEnvironment, bool includeParentEnvironment,
bool runInShell, bool runInShell,
Encoding stderrEncoding, Encoding? stderrEncoding,
Encoding stdoutEncoding, Encoding? stdoutEncoding,
String workingDirectory, String? workingDirectory,
}); });
/// Class for test runner to interact with Flutter's infrastructure service, Cocoon. /// Class for test runner to interact with Flutter's infrastructure service, Cocoon.
...@@ -34,8 +32,8 @@ typedef ProcessRunSync = ProcessResult Function( ...@@ -34,8 +32,8 @@ typedef ProcessRunSync = ProcessResult Function(
/// To retrieve these results, the test runner needs to send results back so the database can be updated. /// To retrieve these results, the test runner needs to send results back so the database can be updated.
class Cocoon { class Cocoon {
Cocoon({ Cocoon({
String serviceAccountTokenPath, String? serviceAccountTokenPath,
@visibleForTesting Client httpClient, @visibleForTesting Client? httpClient,
@visibleForTesting this.fs = const LocalFileSystem(), @visibleForTesting this.fs = const LocalFileSystem(),
@visibleForTesting this.processRunSync = Process.runSync, @visibleForTesting this.processRunSync = Process.runSync,
@visibleForTesting this.requestRetryLimit = 5, @visibleForTesting this.requestRetryLimit = 5,
...@@ -58,7 +56,7 @@ class Cocoon { ...@@ -58,7 +56,7 @@ class Cocoon {
final int requestRetryLimit; final int requestRetryLimit;
String get commitSha => _commitSha ?? _readCommitSha(); String get commitSha => _commitSha ?? _readCommitSha();
String _commitSha; String? _commitSha;
/// Parse the local repo for the current running commit. /// Parse the local repo for the current running commit.
String _readCommitSha() { String _readCommitSha() {
...@@ -85,9 +83,9 @@ class Cocoon { ...@@ -85,9 +83,9 @@ class Cocoon {
/// Send [TaskResult] to Cocoon. /// Send [TaskResult] to Cocoon.
// TODO(chillers): Remove when sendResultsPath is used in prod. https://github.com/flutter/flutter/issues/72457 // TODO(chillers): Remove when sendResultsPath is used in prod. https://github.com/flutter/flutter/issues/72457
Future<void> sendTaskResult({ Future<void> sendTaskResult({
@required String builderName, required String builderName,
@required TaskResult result, required TaskResult result,
@required String gitBranch, required String gitBranch,
}) async { }) async {
assert(builderName != null); assert(builderName != null);
assert(gitBranch != null); assert(gitBranch != null);
...@@ -109,16 +107,11 @@ class Cocoon { ...@@ -109,16 +107,11 @@ class Cocoon {
/// Write the given parameters into an update task request and store the JSON in [resultsPath]. /// Write the given parameters into an update task request and store the JSON in [resultsPath].
Future<void> writeTaskResultToFile({ Future<void> writeTaskResultToFile({
@required String builderName, String? builderName,
@required String gitBranch, String? gitBranch,
@required TaskResult result, required TaskResult result,
@required String resultsPath, required String resultsPath,
}) async { }) async {
assert(builderName != null);
assert(gitBranch != null);
assert(result != null);
assert(resultsPath != null);
final Map<String, dynamic> updateRequest = _constructUpdateRequest( final Map<String, dynamic> updateRequest = _constructUpdateRequest(
gitBranch: gitBranch, gitBranch: gitBranch,
builderName: builderName, builderName: builderName,
...@@ -134,9 +127,9 @@ class Cocoon { ...@@ -134,9 +127,9 @@ class Cocoon {
} }
Map<String, dynamic> _constructUpdateRequest({ Map<String, dynamic> _constructUpdateRequest({
@required String builderName, String? builderName,
@required TaskResult result, required TaskResult result,
@required String gitBranch, String? gitBranch,
}) { }) {
final Map<String, dynamic> updateRequest = <String, dynamic>{ final Map<String, dynamic> updateRequest = <String, dynamic>{
'CommitBranch': gitBranch, 'CommitBranch': gitBranch,
...@@ -151,12 +144,12 @@ class Cocoon { ...@@ -151,12 +144,12 @@ class Cocoon {
final List<String> validScoreKeys = <String>[]; final List<String> validScoreKeys = <String>[];
if (result.benchmarkScoreKeys != null) { if (result.benchmarkScoreKeys != null) {
for (final String scoreKey in result.benchmarkScoreKeys) { for (final String scoreKey in result.benchmarkScoreKeys!) {
final Object score = result.data[scoreKey]; final Object score = result.data![scoreKey] as Object;
if (score is num) { if (score is num) {
// Convert all metrics to double, which provide plenty of precision // Convert all metrics to double, which provide plenty of precision
// without having to add support for multiple numeric types in Cocoon. // without having to add support for multiple numeric types in Cocoon.
result.data[scoreKey] = score.toDouble(); result.data![scoreKey] = score.toDouble();
validScoreKeys.add(scoreKey); validScoreKeys.add(scoreKey);
} }
} }
...@@ -195,15 +188,15 @@ class Cocoon { ...@@ -195,15 +188,15 @@ class Cocoon {
class AuthenticatedCocoonClient extends BaseClient { class AuthenticatedCocoonClient extends BaseClient {
AuthenticatedCocoonClient( AuthenticatedCocoonClient(
this._serviceAccountTokenPath, { this._serviceAccountTokenPath, {
@visibleForTesting Client httpClient, @visibleForTesting Client? httpClient,
@visibleForTesting FileSystem filesystem, @visibleForTesting FileSystem? filesystem,
}) : _delegate = httpClient ?? Client(), }) : _delegate = httpClient ?? Client(),
_fs = filesystem ?? const LocalFileSystem(); _fs = filesystem ?? const LocalFileSystem();
/// Authentication token to have the ability to upload and record test results. /// Authentication token to have the ability to upload and record test results.
/// ///
/// This is intended to only be passed on automated runs on LUCI post-submit. /// This is intended to only be passed on automated runs on LUCI post-submit.
final String _serviceAccountTokenPath; final String? _serviceAccountTokenPath;
/// Underlying [HttpClient] to send requests to. /// Underlying [HttpClient] to send requests to.
final Client _delegate; final Client _delegate;
...@@ -213,7 +206,7 @@ class AuthenticatedCocoonClient extends BaseClient { ...@@ -213,7 +206,7 @@ class AuthenticatedCocoonClient extends BaseClient {
/// Value contained in the service account token file that can be used in http requests. /// Value contained in the service account token file that can be used in http requests.
String get serviceAccountToken => _serviceAccountToken ?? _readServiceAccountTokenFile(); String get serviceAccountToken => _serviceAccountToken ?? _readServiceAccountTokenFile();
String _serviceAccountToken; String? _serviceAccountToken;
/// Get [serviceAccountToken] from the given service account file. /// Get [serviceAccountToken] from the given service account file.
String _readServiceAccountTokenFile() { String _readServiceAccountTokenFile() {
......
...@@ -2,15 +2,12 @@ ...@@ -2,15 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'dart:math' as math; import 'dart:math' as math;
import 'package:flutter_devicelab/common.dart'; import 'package:flutter_devicelab/common.dart';
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'utils.dart'; import 'utils.dart';
...@@ -37,8 +34,8 @@ String getArtifactPath() { ...@@ -37,8 +34,8 @@ String getArtifactPath() {
} }
/// Return the item is in idList if find a match, otherwise return null /// Return the item is in idList if find a match, otherwise return null
String _findMatchId(List<String> idList, String idPattern) { String? _findMatchId(List<String> idList, String idPattern) {
String candidate; String? candidate;
idPattern = idPattern.toLowerCase(); idPattern = idPattern.toLowerCase();
for(final String id in idList) { for(final String id in idList) {
if (id.toLowerCase() == idPattern) { if (id.toLowerCase() == idPattern) {
...@@ -184,47 +181,46 @@ enum AndroidCPU { ...@@ -184,47 +181,46 @@ enum AndroidCPU {
} }
class AndroidDeviceDiscovery implements DeviceDiscovery { class AndroidDeviceDiscovery implements DeviceDiscovery {
factory AndroidDeviceDiscovery({AndroidCPU cpu}) { factory AndroidDeviceDiscovery({AndroidCPU? cpu}) {
return _instance ??= AndroidDeviceDiscovery._(cpu); return _instance ??= AndroidDeviceDiscovery._(cpu);
} }
AndroidDeviceDiscovery._(this.cpu); AndroidDeviceDiscovery._(this.cpu);
final AndroidCPU cpu; final AndroidCPU? cpu;
// Parses information about a device. Example: // Parses information about a device. Example:
// //
// 015d172c98400a03 device usb:340787200X product:nakasi model:Nexus_7 device:grouper // 015d172c98400a03 device usb:340787200X product:nakasi model:Nexus_7 device:grouper
static final RegExp _kDeviceRegex = RegExp(r'^(\S+)\s+(\S+)(.*)'); static final RegExp _kDeviceRegex = RegExp(r'^(\S+)\s+(\S+)(.*)');
static AndroidDeviceDiscovery _instance; static AndroidDeviceDiscovery? _instance;
AndroidDevice _workingDevice; AndroidDevice? _workingDevice;
@override @override
Future<AndroidDevice> get workingDevice async { Future<AndroidDevice> get workingDevice async {
if (_workingDevice == null) { if (_workingDevice == null) {
if (Platform.environment.containsKey(DeviceIdEnvName)) { if (Platform.environment.containsKey(DeviceIdEnvName)) {
final String deviceId = Platform.environment[DeviceIdEnvName]; final String deviceId = Platform.environment[DeviceIdEnvName]!;
await chooseWorkingDeviceById(deviceId); await chooseWorkingDeviceById(deviceId);
return _workingDevice; return _workingDevice!;
} }
await chooseWorkingDevice(); await chooseWorkingDevice();
} }
return _workingDevice; return _workingDevice!;
} }
Future<bool> _matchesCPURequirement(AndroidDevice device) async { Future<bool> _matchesCPURequirement(AndroidDevice device) async {
if (cpu == null)
return true;
switch (cpu) { switch (cpu) {
case null:
return true;
case AndroidCPU.arm64: case AndroidCPU.arm64:
return device.isArm64(); return device.isArm64();
case AndroidCPU.arm: case AndroidCPU.arm:
return device.isArm(); return device.isArm();
} }
return true;
} }
/// Picks a random Android device out of connected devices and sets it as /// Picks a random Android device out of connected devices and sets it as
...@@ -259,11 +255,11 @@ class AndroidDeviceDiscovery implements DeviceDiscovery { ...@@ -259,11 +255,11 @@ class AndroidDeviceDiscovery implements DeviceDiscovery {
@override @override
Future<void> chooseWorkingDeviceById(String deviceId) async { Future<void> chooseWorkingDeviceById(String deviceId) async {
final String matchedId = _findMatchId(await discoverDevices(), deviceId); final String? matchedId = _findMatchId(await discoverDevices(), deviceId);
if (matchedId != null) { if (matchedId != null) {
_workingDevice = AndroidDevice(deviceId: matchedId); _workingDevice = AndroidDevice(deviceId: matchedId);
if (cpu != null) { if (cpu != null) {
if (!await _matchesCPURequirement(_workingDevice)) { if (!await _matchesCPURequirement(_workingDevice!)) {
throw DeviceException('The selected device $matchedId does not match the cpu requirement'); throw DeviceException('The selected device $matchedId does not match the cpu requirement');
} }
} }
...@@ -290,10 +286,10 @@ class AndroidDeviceDiscovery implements DeviceDiscovery { ...@@ -290,10 +286,10 @@ class AndroidDeviceDiscovery implements DeviceDiscovery {
continue; continue;
if (_kDeviceRegex.hasMatch(line)) { if (_kDeviceRegex.hasMatch(line)) {
final Match match = _kDeviceRegex.firstMatch(line); final Match match = _kDeviceRegex.firstMatch(line)!;
final String deviceID = match[1]; final String deviceID = match[1]!;
final String deviceState = match[2]; final String deviceState = match[2]!;
if (!const <String>['unauthorized', 'offline'].contains(deviceState)) { if (!const <String>['unauthorized', 'offline'].contains(deviceState)) {
results.add(deviceID); results.add(deviceID);
...@@ -342,9 +338,9 @@ class FuchsiaDeviceDiscovery implements DeviceDiscovery { ...@@ -342,9 +338,9 @@ class FuchsiaDeviceDiscovery implements DeviceDiscovery {
FuchsiaDeviceDiscovery._(); FuchsiaDeviceDiscovery._();
static FuchsiaDeviceDiscovery _instance; static FuchsiaDeviceDiscovery? _instance;
FuchsiaDevice _workingDevice; FuchsiaDevice? _workingDevice;
String get _ffx { String get _ffx {
final String ffx = path.join(getArtifactPath(), 'fuchsia', 'tools','x64', 'ffx'); final String ffx = path.join(getArtifactPath(), 'fuchsia', 'tools','x64', 'ffx');
...@@ -358,13 +354,13 @@ class FuchsiaDeviceDiscovery implements DeviceDiscovery { ...@@ -358,13 +354,13 @@ class FuchsiaDeviceDiscovery implements DeviceDiscovery {
Future<FuchsiaDevice> get workingDevice async { Future<FuchsiaDevice> get workingDevice async {
if (_workingDevice == null) { if (_workingDevice == null) {
if (Platform.environment.containsKey(DeviceIdEnvName)) { if (Platform.environment.containsKey(DeviceIdEnvName)) {
final String deviceId = Platform.environment[DeviceIdEnvName]; final String deviceId = Platform.environment[DeviceIdEnvName]!;
await chooseWorkingDeviceById(deviceId); await chooseWorkingDeviceById(deviceId);
return _workingDevice; return _workingDevice!;
} }
await chooseWorkingDevice(); await chooseWorkingDevice();
} }
return _workingDevice; return _workingDevice!;
} }
/// Picks the first connected Fuchsia device. /// Picks the first connected Fuchsia device.
...@@ -383,8 +379,8 @@ class FuchsiaDeviceDiscovery implements DeviceDiscovery { ...@@ -383,8 +379,8 @@ class FuchsiaDeviceDiscovery implements DeviceDiscovery {
@override @override
Future<void> chooseWorkingDeviceById(String deviceId) async { Future<void> chooseWorkingDeviceById(String deviceId) async {
final String matchedId = _findMatchId(await discoverDevices(), deviceId); final String? matchedId = _findMatchId(await discoverDevices(), deviceId);
if (deviceId != null) { if (matchedId != null) {
_workingDevice = FuchsiaDevice(deviceId: matchedId); _workingDevice = FuchsiaDevice(deviceId: matchedId);
print('Choose device by ID: $matchedId'); print('Choose device by ID: $matchedId');
return; return;
...@@ -442,7 +438,7 @@ class FuchsiaDeviceDiscovery implements DeviceDiscovery { ...@@ -442,7 +438,7 @@ class FuchsiaDeviceDiscovery implements DeviceDiscovery {
} }
class AndroidDevice extends Device { class AndroidDevice extends Device {
AndroidDevice({@required this.deviceId}) { AndroidDevice({required this.deviceId}) {
_updateDeviceInfo(); _updateDeviceInfo();
} }
...@@ -540,19 +536,19 @@ class AndroidDevice extends Device { ...@@ -540,19 +536,19 @@ class AndroidDevice extends Device {
} }
/// Executes [command] on `adb shell` and returns its exit code. /// Executes [command] on `adb shell` and returns its exit code.
Future<void> shellExec(String command, List<String> arguments, { Map<String, String> environment, bool silent = false }) async { Future<void> shellExec(String command, List<String> arguments, { Map<String, String>? environment, bool silent = false }) async {
await adb(<String>['shell', command, ...arguments], environment: environment, silent: silent); await adb(<String>['shell', command, ...arguments], environment: environment, silent: silent);
} }
/// Executes [command] on `adb shell` and returns its standard output as a [String]. /// Executes [command] on `adb shell` and returns its standard output as a [String].
Future<String> shellEval(String command, List<String> arguments, { Map<String, String> environment, bool silent = false }) { Future<String> shellEval(String command, List<String> arguments, { Map<String, String>? environment, bool silent = false }) {
return adb(<String>['shell', command, ...arguments], environment: environment, silent: silent); return adb(<String>['shell', command, ...arguments], environment: environment, silent: silent);
} }
/// Runs `adb` with the given [arguments], selecting this device. /// Runs `adb` with the given [arguments], selecting this device.
Future<String> adb( Future<String> adb(
List<String> arguments, { List<String> arguments, {
Map<String, String> environment, Map<String, String>? environment,
bool silent = false, bool silent = false,
}) { }) {
return eval( return eval(
...@@ -568,18 +564,18 @@ class AndroidDevice extends Device { ...@@ -568,18 +564,18 @@ class AndroidDevice extends Device {
@override @override
Future<Map<String, dynamic>> getMemoryStats(String packageName) async { Future<Map<String, dynamic>> getMemoryStats(String packageName) async {
final String meminfo = await shellEval('dumpsys', <String>['meminfo', packageName]); final String meminfo = await shellEval('dumpsys', <String>['meminfo', packageName]);
final Match match = RegExp(r'TOTAL\s+(\d+)').firstMatch(meminfo); final Match? match = RegExp(r'TOTAL\s+(\d+)').firstMatch(meminfo);
assert(match != null, 'could not parse dumpsys meminfo output'); assert(match != null, 'could not parse dumpsys meminfo output');
return <String, dynamic>{ return <String, dynamic>{
'total_kb': int.parse(match.group(1)), 'total_kb': int.parse(match!.group(1)!),
}; };
} }
@override @override
bool get canStreamLogs => true; bool get canStreamLogs => true;
bool _abortedLogging/*!*/ = false; bool _abortedLogging = false;
Process/*?*/ _loggingProcess; Process? _loggingProcess;
@override @override
Future<void> startLoggingToSink(IOSink sink, {bool clear = true}) async { Future<void> startLoggingToSink(IOSink sink, {bool clear = true}) async {
...@@ -596,17 +592,17 @@ class AndroidDevice extends Device { ...@@ -596,17 +592,17 @@ class AndroidDevice extends Device {
// to view the whole log, or just run logcat alongside this. // to view the whole log, or just run logcat alongside this.
<String>['-s', deviceId, 'logcat', 'ActivityManager:I', 'flutter:V', '*:F'], <String>['-s', deviceId, 'logcat', 'ActivityManager:I', 'flutter:V', '*:F'],
); );
_loggingProcess.stdout _loggingProcess!.stdout
.transform<String>(const Utf8Decoder(allowMalformed: true)) .transform<String>(const Utf8Decoder(allowMalformed: true))
.listen((String line) { .listen((String line) {
sink.write(line); sink.write(line);
}); });
_loggingProcess.stderr _loggingProcess!.stderr
.transform<String>(const Utf8Decoder(allowMalformed: true)) .transform<String>(const Utf8Decoder(allowMalformed: true))
.listen((String line) { .listen((String line) {
sink.write(line); sink.write(line);
}); });
unawaited(_loggingProcess.exitCode.then<void>((int exitCode) { unawaited(_loggingProcess!.exitCode.then<void>((int exitCode) {
if (!_abortedLogging) { if (!_abortedLogging) {
sink.writeln('adb logcat failed with exit code $exitCode.\n'); sink.writeln('adb logcat failed with exit code $exitCode.\n');
} }
...@@ -617,8 +613,8 @@ class AndroidDevice extends Device { ...@@ -617,8 +613,8 @@ class AndroidDevice extends Device {
Future<void> stopLoggingToSink() async { Future<void> stopLoggingToSink() async {
if (_loggingProcess != null) { if (_loggingProcess != null) {
_abortedLogging = true; _abortedLogging = true;
_loggingProcess.kill(); _loggingProcess!.kill();
await _loggingProcess.exitCode; await _loggingProcess!.exitCode;
} }
} }
...@@ -629,7 +625,7 @@ class AndroidDevice extends Device { ...@@ -629,7 +625,7 @@ class AndroidDevice extends Device {
final Completer<void> processDone = Completer<void>(); final Completer<void> processDone = Completer<void>();
final Completer<void> abort = Completer<void>(); final Completer<void> abort = Completer<void>();
bool aborted = false; bool aborted = false;
StreamController<String> stream; late final StreamController<String> stream;
stream = StreamController<String>( stream = StreamController<String>(
onListen: () async { onListen: () async {
await adb(<String>['logcat', '--clear']); await adb(<String>['logcat', '--clear']);
...@@ -713,22 +709,22 @@ class IosDeviceDiscovery implements DeviceDiscovery { ...@@ -713,22 +709,22 @@ class IosDeviceDiscovery implements DeviceDiscovery {
IosDeviceDiscovery._(); IosDeviceDiscovery._();
static IosDeviceDiscovery _instance; static IosDeviceDiscovery? _instance;
IosDevice _workingDevice; IosDevice? _workingDevice;
@override @override
Future<IosDevice> get workingDevice async { Future<IosDevice> get workingDevice async {
if (_workingDevice == null) { if (_workingDevice == null) {
if (Platform.environment.containsKey(DeviceIdEnvName)) { if (Platform.environment.containsKey(DeviceIdEnvName)) {
final String deviceId = Platform.environment[DeviceIdEnvName]; final String deviceId = Platform.environment[DeviceIdEnvName]!;
await chooseWorkingDeviceById(deviceId); await chooseWorkingDeviceById(deviceId);
return _workingDevice; return _workingDevice!;
} }
await chooseWorkingDevice(); await chooseWorkingDevice();
} }
return _workingDevice; return _workingDevice!;
} }
/// Picks a random iOS device out of connected devices and sets it as /// Picks a random iOS device out of connected devices and sets it as
...@@ -749,7 +745,7 @@ class IosDeviceDiscovery implements DeviceDiscovery { ...@@ -749,7 +745,7 @@ class IosDeviceDiscovery implements DeviceDiscovery {
@override @override
Future<void> chooseWorkingDeviceById(String deviceId) async { Future<void> chooseWorkingDeviceById(String deviceId) async {
final String matchedId = _findMatchId(await discoverDevices(), deviceId); final String? matchedId = _findMatchId(await discoverDevices(), deviceId);
if (matchedId != null) { if (matchedId != null) {
_workingDevice = IosDevice(deviceId: matchedId); _workingDevice = IosDevice(deviceId: matchedId);
print('Choose device by ID: $matchedId'); print('Choose device by ID: $matchedId');
...@@ -824,7 +820,7 @@ class IosDeviceDiscovery implements DeviceDiscovery { ...@@ -824,7 +820,7 @@ class IosDeviceDiscovery implements DeviceDiscovery {
/// iOS device. /// iOS device.
class IosDevice extends Device { class IosDevice extends Device {
IosDevice({ @required this.deviceId }); IosDevice({ required this.deviceId });
@override @override
final String deviceId; final String deviceId;
...@@ -846,8 +842,8 @@ class IosDevice extends Device { ...@@ -846,8 +842,8 @@ class IosDevice extends Device {
@override @override
bool get canStreamLogs => true; bool get canStreamLogs => true;
bool _abortedLogging/*!*/ = false; bool _abortedLogging = false;
Process/*?*/ _loggingProcess; Process? _loggingProcess;
@override @override
Future<void> startLoggingToSink(IOSink sink, {bool clear = true}) async { Future<void> startLoggingToSink(IOSink sink, {bool clear = true}) async {
...@@ -859,17 +855,17 @@ class IosDevice extends Device { ...@@ -859,17 +855,17 @@ class IosDevice extends Device {
'DYLD_LIBRARY_PATH': dyldLibraryPath, 'DYLD_LIBRARY_PATH': dyldLibraryPath,
}, },
); );
_loggingProcess.stdout _loggingProcess!.stdout
.transform<String>(const Utf8Decoder(allowMalformed: true)) .transform<String>(const Utf8Decoder(allowMalformed: true))
.listen((String line) { .listen((String line) {
sink.write(line); sink.write(line);
}); });
_loggingProcess.stderr _loggingProcess!.stderr
.transform<String>(const Utf8Decoder(allowMalformed: true)) .transform<String>(const Utf8Decoder(allowMalformed: true))
.listen((String line) { .listen((String line) {
sink.write(line); sink.write(line);
}); });
unawaited(_loggingProcess.exitCode.then<void>((int exitCode) { unawaited(_loggingProcess!.exitCode.then<void>((int exitCode) {
if (!_abortedLogging) { if (!_abortedLogging) {
sink.writeln('idevicesyslog failed with exit code $exitCode.\n'); sink.writeln('idevicesyslog failed with exit code $exitCode.\n');
} }
...@@ -880,8 +876,8 @@ class IosDevice extends Device { ...@@ -880,8 +876,8 @@ class IosDevice extends Device {
Future<void> stopLoggingToSink() async { Future<void> stopLoggingToSink() async {
if (_loggingProcess != null) { if (_loggingProcess != null) {
_abortedLogging = true; _abortedLogging = true;
_loggingProcess.kill(); _loggingProcess!.kill();
await _loggingProcess.exitCode; await _loggingProcess!.exitCode;
} }
} }
...@@ -934,7 +930,7 @@ class IosDevice extends Device { ...@@ -934,7 +930,7 @@ class IosDevice extends Device {
/// Fuchsia device. /// Fuchsia device.
class FuchsiaDevice extends Device { class FuchsiaDevice extends Device {
const FuchsiaDevice({ @required this.deviceId }); const FuchsiaDevice({ required this.deviceId });
@override @override
final String deviceId; final String deviceId;
...@@ -982,7 +978,7 @@ class FuchsiaDevice extends Device { ...@@ -982,7 +978,7 @@ class FuchsiaDevice extends Device {
/// Path to the `adb` executable. /// Path to the `adb` executable.
String get adbPath { String get adbPath {
final String androidHome = Platform.environment['ANDROID_HOME'] ?? Platform.environment['ANDROID_SDK_ROOT']; final String? androidHome = Platform.environment['ANDROID_HOME'] ?? Platform.environment['ANDROID_SDK_ROOT'];
if (androidHome == null) { if (androidHome == null) {
throw const DeviceException( throw const DeviceException(
...@@ -1001,7 +997,7 @@ String get adbPath { ...@@ -1001,7 +997,7 @@ String get adbPath {
} }
class FakeDevice extends Device { class FakeDevice extends Device {
const FakeDevice({ @required this.deviceId }); const FakeDevice({ required this.deviceId });
@override @override
final String deviceId; final String deviceId;
...@@ -1055,22 +1051,22 @@ class FakeDeviceDiscovery implements DeviceDiscovery { ...@@ -1055,22 +1051,22 @@ class FakeDeviceDiscovery implements DeviceDiscovery {
FakeDeviceDiscovery._(); FakeDeviceDiscovery._();
static FakeDeviceDiscovery _instance; static FakeDeviceDiscovery? _instance;
FakeDevice _workingDevice; FakeDevice? _workingDevice;
@override @override
Future<FakeDevice> get workingDevice async { Future<FakeDevice> get workingDevice async {
if (_workingDevice == null) { if (_workingDevice == null) {
if (Platform.environment.containsKey(DeviceIdEnvName)) { if (Platform.environment.containsKey(DeviceIdEnvName)) {
final String deviceId = Platform.environment[DeviceIdEnvName]; final String deviceId = Platform.environment[DeviceIdEnvName]!;
await chooseWorkingDeviceById(deviceId); await chooseWorkingDeviceById(deviceId);
return _workingDevice; return _workingDevice!;
} }
await chooseWorkingDevice(); await chooseWorkingDevice();
} }
return _workingDevice; return _workingDevice!;
} }
/// The Fake is only available for by ID device discovery. /// The Fake is only available for by ID device discovery.
...@@ -1081,7 +1077,7 @@ class FakeDeviceDiscovery implements DeviceDiscovery { ...@@ -1081,7 +1077,7 @@ class FakeDeviceDiscovery implements DeviceDiscovery {
@override @override
Future<void> chooseWorkingDeviceById(String deviceId) async { Future<void> chooseWorkingDeviceById(String deviceId) async {
final String matchedId = _findMatchId(await discoverDevices(), deviceId); final String? matchedId = _findMatchId(await discoverDevices(), deviceId);
if (matchedId != null) { if (matchedId != null) {
_workingDevice = FakeDevice(deviceId: matchedId); _workingDevice = FakeDevice(deviceId: matchedId);
print('Choose device by ID: $matchedId'); print('Choose device by ID: $matchedId');
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
...@@ -64,8 +62,8 @@ class _TaskRunner { ...@@ -64,8 +62,8 @@ class _TaskRunner {
_TaskRunner(this.task) { _TaskRunner(this.task) {
registerExtension('ext.cocoonRunTask', registerExtension('ext.cocoonRunTask',
(String method, Map<String, String> parameters) async { (String method, Map<String, String> parameters) async {
final Duration taskTimeout = parameters.containsKey('timeoutInMinutes') final Duration? taskTimeout = parameters.containsKey('timeoutInMinutes')
? Duration(minutes: int.parse(parameters['timeoutInMinutes'])) ? Duration(minutes: int.parse(parameters['timeoutInMinutes']!))
: null; : null;
// This is only expected to be passed in unit test runs so they do not // This is only expected to be passed in unit test runs so they do not
// kill the Dart process that is running them and waste time running config. // kill the Dart process that is running them and waste time running config.
...@@ -82,7 +80,7 @@ class _TaskRunner { ...@@ -82,7 +80,7 @@ class _TaskRunner {
final TaskFunction task; final TaskFunction task;
Future<Device/*?*/> _getWorkingDeviceIfAvailable() async { Future<Device?> _getWorkingDeviceIfAvailable() async {
try { try {
return await devices.workingDevice; return await devices.workingDevice;
} on DeviceException { } on DeviceException {
...@@ -91,8 +89,8 @@ class _TaskRunner { ...@@ -91,8 +89,8 @@ class _TaskRunner {
} }
// TODO(ianh): workaround for https://github.com/dart-lang/sdk/issues/23797 // TODO(ianh): workaround for https://github.com/dart-lang/sdk/issues/23797
RawReceivePort _keepAlivePort; RawReceivePort? _keepAlivePort;
Timer _startTaskTimeout; Timer? _startTaskTimeout;
bool _taskStarted = false; bool _taskStarted = false;
final Completer<TaskResult> _completer = Completer<TaskResult>(); final Completer<TaskResult> _completer = Completer<TaskResult>();
...@@ -102,7 +100,7 @@ class _TaskRunner { ...@@ -102,7 +100,7 @@ class _TaskRunner {
/// Signals that this task runner finished running the task. /// Signals that this task runner finished running the task.
Future<TaskResult> get whenDone => _completer.future; Future<TaskResult> get whenDone => _completer.future;
Future<TaskResult> run(Duration taskTimeout, { Future<TaskResult> run(Duration? taskTimeout, {
bool runFlutterConfig = true, bool runFlutterConfig = true,
bool runProcessCleanup = true, bool runProcessCleanup = true,
}) async { }) async {
...@@ -110,7 +108,7 @@ class _TaskRunner { ...@@ -110,7 +108,7 @@ class _TaskRunner {
_taskStarted = true; _taskStarted = true;
print('Running task with a timeout of $taskTimeout.'); print('Running task with a timeout of $taskTimeout.');
final String exe = Platform.isWindows ? '.exe' : ''; final String exe = Platform.isWindows ? '.exe' : '';
Set<RunningProcessInfo> beforeRunningDartInstances; late Set<RunningProcessInfo> beforeRunningDartInstances;
if (runProcessCleanup) { if (runProcessCleanup) {
section('Checking running Dart$exe processes'); section('Checking running Dart$exe processes');
beforeRunningDartInstances = await getRunningProcesses( beforeRunningDartInstances = await getRunningProcesses(
...@@ -136,7 +134,7 @@ class _TaskRunner { ...@@ -136,7 +134,7 @@ class _TaskRunner {
'--enable-windows-desktop', '--enable-windows-desktop',
'--enable-linux-desktop', '--enable-linux-desktop',
'--enable-web', '--enable-web',
if (localEngine != null) ...<String>['--local-engine', localEngine], if (localEngine != null) ...<String>['--local-engine', localEngine!],
], canFail: true); ], canFail: true);
if (configResult != 0) { if (configResult != 0) {
print('Failed to enable configuration, tasks may not run.'); print('Failed to enable configuration, tasks may not run.');
...@@ -145,12 +143,12 @@ class _TaskRunner { ...@@ -145,12 +143,12 @@ class _TaskRunner {
print('Skipping enabling configs for macOS, Linux, Windows, and Web'); print('Skipping enabling configs for macOS, Linux, Windows, and Web');
} }
final Device/*?*/ device = await _getWorkingDeviceIfAvailable(); final Device? device = await _getWorkingDeviceIfAvailable();
/*late*/ TaskResult result; late TaskResult result;
IOSink/*?*/ sink; IOSink? sink;
try { try {
if (device != null && device.canStreamLogs && hostAgent.dumpDirectory != null) { if (device != null && device.canStreamLogs && hostAgent.dumpDirectory != null) {
sink = File(path.join(hostAgent.dumpDirectory.path, '${device.deviceId}.log')).openWrite(); sink = File(path.join(hostAgent.dumpDirectory!.path, '${device.deviceId}.log')).openWrite();
await device.startLoggingToSink(sink); await device.startLoggingToSink(sink);
} }
...@@ -212,7 +210,7 @@ class _TaskRunner { ...@@ -212,7 +210,7 @@ class _TaskRunner {
final File rebootFile = _rebootFile(); final File rebootFile = _rebootFile();
int runCount; int runCount;
if (rebootFile.existsSync()) { if (rebootFile.existsSync()) {
runCount = int.tryParse(rebootFile.readAsStringSync().trim()); runCount = int.tryParse(rebootFile.readAsStringSync().trim()) ?? 0;
} else { } else {
runCount = 0; runCount = 0;
} }
...@@ -283,10 +281,10 @@ class _TaskRunner { ...@@ -283,10 +281,10 @@ class _TaskRunner {
File _rebootFile() { File _rebootFile() {
if (Platform.isLinux || Platform.isMacOS) { if (Platform.isLinux || Platform.isMacOS) {
return File(path.join(Platform.environment['HOME'], '.reboot-count')); return File(path.join(Platform.environment['HOME']!, '.reboot-count'));
} }
if (!Platform.isWindows) { if (!Platform.isWindows) {
throw StateError('Unexpected platform ${Platform.operatingSystem}'); throw StateError('Unexpected platform ${Platform.operatingSystem}');
} }
return File(path.join(Platform.environment['USERPROFILE'], '.reboot-count')); return File(path.join(Platform.environment['USERPROFILE']!, '.reboot-count'));
} }
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:convert'; import 'dart:convert';
import 'utils.dart'; import 'utils.dart';
...@@ -42,17 +40,10 @@ Future<bool> containsBitcode(String pathToBinary) async { ...@@ -42,17 +40,10 @@ Future<bool> containsBitcode(String pathToBinary) async {
final List<String> lines = LineSplitter.split(loadCommands).toList(); final List<String> lines = LineSplitter.split(loadCommands).toList();
lines.asMap().forEach((int index, String line) { lines.asMap().forEach((int index, String line) {
if (line.contains('segname __LLVM') && lines.length - index - 1 > 3) { if (line.contains('segname __LLVM') && lines.length - index - 1 > 3) {
final String emptyBitcodeMarker = lines emptyBitcodeMarkerFound |= lines
.skip(index - 1) .skip(index - 1)
.take(4) .take(4)
.firstWhere( .any((String line) => line.contains(' size 0x0000000000000001'));
(String line) => line.contains(' size 0x0000000000000001'),
orElse: () => null,
);
if (emptyBitcodeMarker != null) {
emptyBitcodeMarkerFound = true;
return;
}
} }
}); });
return !emptyBitcodeMarkerFound; return !emptyBitcodeMarkerFound;
...@@ -79,16 +70,16 @@ Future<void> testWithNewIOSSimulator( ...@@ -79,16 +70,16 @@ Future<void> testWithNewIOSSimulator(
workingDirectory: flutterDirectory.path, workingDirectory: flutterDirectory.path,
); );
String iOSSimRuntime; String? iOSSimRuntime;
final RegExp iOSRuntimePattern = RegExp(r'iOS .*\) - (.*)'); final RegExp iOSRuntimePattern = RegExp(r'iOS .*\) - (.*)');
for (final String runtime in LineSplitter.split(availableRuntimes)) { for (final String runtime in LineSplitter.split(availableRuntimes)) {
// These seem to be in order, so allow matching multiple lines so it grabs // These seem to be in order, so allow matching multiple lines so it grabs
// the last (hopefully latest) one. // the last (hopefully latest) one.
final RegExpMatch iOSRuntimeMatch = iOSRuntimePattern.firstMatch(runtime); final RegExpMatch? iOSRuntimeMatch = iOSRuntimePattern.firstMatch(runtime);
if (iOSRuntimeMatch != null) { if (iOSRuntimeMatch != null) {
iOSSimRuntime = iOSRuntimeMatch.group(1).trim(); iOSSimRuntime = iOSRuntimeMatch.group(1)!.trim();
continue; continue;
} }
} }
......
...@@ -2,17 +2,14 @@ ...@@ -2,17 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:io'; import 'dart:io';
import 'package:meta/meta.dart';
import 'package:yaml/yaml.dart'; import 'package:yaml/yaml.dart';
import 'utils.dart'; import 'utils.dart';
/// Loads manifest data from `manifest.yaml` file or from [yaml], if present. /// Loads manifest data from `manifest.yaml` file or from [yaml], if present.
Manifest loadTaskManifest([ String yaml ]) { Manifest loadTaskManifest([ String? yaml ]) {
final dynamic manifestYaml = yaml == null final dynamic manifestYaml = yaml == null
? loadYaml(file('manifest.yaml').readAsStringSync()) ? loadYaml(file('manifest.yaml').readAsStringSync())
: loadYamlNode(yaml); : loadYamlNode(yaml);
...@@ -32,13 +29,13 @@ class Manifest { ...@@ -32,13 +29,13 @@ class Manifest {
/// A CI task. /// A CI task.
class ManifestTask { class ManifestTask {
ManifestTask._({ ManifestTask._({
@required this.name, required this.name,
@required this.description, required this.description,
@required this.stage, required this.stage,
@required this.requiredAgentCapabilities, required this.requiredAgentCapabilities,
@required this.isFlaky, required this.isFlaky,
@required this.timeoutInMinutes, required this.timeoutInMinutes,
@required this.onLuci, required this.onLuci,
}) { }) {
final String taskName = 'task "$name"'; final String taskName = 'task "$name"';
_checkIsNotBlank(name, 'Task name', taskName); _checkIsNotBlank(name, 'Task name', taskName);
...@@ -148,9 +145,9 @@ ManifestTask _validateAndParseTask(String taskName, dynamic taskYaml) { ...@@ -148,9 +145,9 @@ ManifestTask _validateAndParseTask(String taskName, dynamic taskYaml) {
// ignore: avoid_dynamic_calls // ignore: avoid_dynamic_calls
stage: taskYaml['stage'] as String, stage: taskYaml['stage'] as String,
requiredAgentCapabilities: capabilities as List<String>, requiredAgentCapabilities: capabilities as List<String>,
isFlaky: isFlaky as bool ?? false, isFlaky: isFlaky as bool,
timeoutInMinutes: timeoutInMinutes as int, timeoutInMinutes: timeoutInMinutes as int,
onLuci: onLuci as bool ?? false, onLuci: onLuci as bool,
); );
} }
...@@ -161,7 +158,7 @@ List<String> _validateAndParseCapabilities(String taskName, dynamic capabilities ...@@ -161,7 +158,7 @@ List<String> _validateAndParseCapabilities(String taskName, dynamic capabilities
final dynamic capability = capabilities[i]; final dynamic capability = capabilities[i];
_checkType(capability is String, capability, 'required_agent_capabilities[$i]', 'string'); _checkType(capability is String, capability, 'required_agent_capabilities[$i]', 'string');
} }
return (capabilitiesYaml as List<dynamic>).cast<String>(); return capabilitiesYaml.cast<String>();
} }
void _checkType(bool isValid, dynamic value, String variableName, String typeName) { void _checkType(bool isValid, dynamic value, String variableName, String typeName) {
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
...@@ -21,13 +19,13 @@ Future<void> runTasks( ...@@ -21,13 +19,13 @@ Future<void> runTasks(
List<String> taskNames, { List<String> taskNames, {
bool exitOnFirstTestFailure = false, bool exitOnFirstTestFailure = false,
bool silent = false, bool silent = false,
String deviceId, String? deviceId,
String gitBranch, String? gitBranch,
String localEngine, String? localEngine,
String localEngineSrcPath, String? localEngineSrcPath,
String luciBuilder, String? luciBuilder,
String resultsPath, String? resultsPath,
List<String> taskArgs, List<String>? taskArgs,
}) async { }) async {
for (final String taskName in taskNames) { for (final String taskName in taskNames) {
section('Running task "$taskName"'); section('Running task "$taskName"');
...@@ -44,7 +42,7 @@ Future<void> runTasks( ...@@ -44,7 +42,7 @@ Future<void> runTasks(
print(const JsonEncoder.withIndent(' ').convert(result)); print(const JsonEncoder.withIndent(' ').convert(result));
section('Finished task "$taskName"'); section('Finished task "$taskName"');
if (resultsPath != null) { if (resultsPath != null && gitBranch != null) {
final Cocoon cocoon = Cocoon(); final Cocoon cocoon = Cocoon();
await cocoon.writeTaskResultToFile( await cocoon.writeTaskResultToFile(
builderName: luciBuilder, builderName: luciBuilder,
...@@ -76,11 +74,11 @@ Future<void> runTasks( ...@@ -76,11 +74,11 @@ Future<void> runTasks(
Future<TaskResult> runTask( Future<TaskResult> runTask(
String taskName, { String taskName, {
bool silent = false, bool silent = false,
String localEngine, String? localEngine,
String localEngineSrcPath, String? localEngineSrcPath,
String deviceId, String? deviceId,
List<String> taskArgs, List<String> ?taskArgs,
@visibleForTesting Map<String, String> isolateParams, @visibleForTesting Map<String, String>? isolateParams,
}) async { }) async {
final String taskExecutable = 'bin/tasks/$taskName.dart'; final String taskExecutable = 'bin/tasks/$taskName.dart';
...@@ -117,7 +115,7 @@ Future<TaskResult> runTask( ...@@ -117,7 +115,7 @@ Future<TaskResult> runTask(
.transform<String>(const LineSplitter()) .transform<String>(const LineSplitter())
.listen((String line) { .listen((String line) {
if (!uri.isCompleted) { if (!uri.isCompleted) {
final Uri serviceUri = parseServiceUri(line, prefix: 'Observatory listening on '); final Uri? serviceUri = parseServiceUri(line, prefix: 'Observatory listening on ');
if (serviceUri != null) if (serviceUri != null)
uri.complete(serviceUri); uri.complete(serviceUri);
} }
...@@ -139,7 +137,7 @@ Future<TaskResult> runTask( ...@@ -139,7 +137,7 @@ Future<TaskResult> runTask(
'ext.cocoonRunTask', 'ext.cocoonRunTask',
args: isolateParams, args: isolateParams,
isolateId: result.isolate.id, isolateId: result.isolate.id,
)).json; )).json!;
final TaskResult taskResult = TaskResult.fromJson(taskResultJson); final TaskResult taskResult = TaskResult.fromJson(taskResultJson);
await runner.exitCode; await runner.exitCode;
return taskResult; return taskResult;
...@@ -168,13 +166,13 @@ Future<ConnectionResult> _connectToRunnerIsolate(Uri vmServiceUri) async { ...@@ -168,13 +166,13 @@ Future<ConnectionResult> _connectToRunnerIsolate(Uri vmServiceUri) async {
// Look up the isolate. // Look up the isolate.
final VmService client = await vmServiceConnectUri(url); final VmService client = await vmServiceConnectUri(url);
VM vm = await client.getVM(); VM vm = await client.getVM();
while (vm.isolates.isEmpty) { while (vm.isolates!.isEmpty) {
await Future<void>.delayed(const Duration(seconds: 1)); await Future<void>.delayed(const Duration(seconds: 1));
vm = await client.getVM(); vm = await client.getVM();
} }
final IsolateRef isolate = vm.isolates.first; final IsolateRef isolate = vm.isolates!.first;
final Response response = await client.callServiceExtension('ext.cocoonRunnerReady', isolateId: isolate.id); final Response response = await client.callServiceExtension('ext.cocoonRunnerReady', isolateId: isolate.id);
if (response.json['response'] != 'ready') if (response.json!['response'] != 'ready')
throw 'not ready yet'; throw 'not ready yet';
return ConnectionResult(client, isolate); return ConnectionResult(client, isolate);
} catch (error) { } catch (error) {
...@@ -193,7 +191,7 @@ class ConnectionResult { ...@@ -193,7 +191,7 @@ class ConnectionResult {
} }
/// The cocoon client sends an invalid VM service response, we need to intercept it. /// The cocoon client sends an invalid VM service response, we need to intercept it.
Future<VmService> vmServiceConnectUri(String wsUri, {Log log}) async { Future<VmService> vmServiceConnectUri(String wsUri, {Log? log}) async {
final WebSocket socket = await WebSocket.connect(wsUri); final WebSocket socket = await WebSocket.connect(wsUri);
final StreamController<dynamic> controller = StreamController<dynamic>(); final StreamController<dynamic> controller = StreamController<dynamic>();
final Completer<dynamic> streamClosedCompleter = Completer<dynamic>(); final Completer<dynamic> streamClosedCompleter = Completer<dynamic>();
...@@ -207,7 +205,7 @@ Future<VmService> vmServiceConnectUri(String wsUri, {Log log}) async { ...@@ -207,7 +205,7 @@ Future<VmService> vmServiceConnectUri(String wsUri, {Log log}) async {
controller.add(data); controller.add(data);
} }
}, },
onError: (dynamic err, StackTrace stackTrace) => controller.addError(err, stackTrace), onError: (Object err, StackTrace stackTrace) => controller.addError(err, stackTrace),
onDone: () => streamClosedCompleter.complete(), onDone: () => streamClosedCompleter.complete(),
); );
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:io'; import 'dart:io';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
...@@ -36,7 +34,7 @@ class RunningProcessInfo { ...@@ -36,7 +34,7 @@ class RunningProcessInfo {
} }
} }
Future<bool> killProcess(String pid, {ProcessManager processManager}) async { Future<bool> killProcess(String pid, {ProcessManager? processManager}) async {
assert(pid != null, 'Must specify a pid to kill'); assert(pid != null, 'Must specify a pid to kill');
processManager ??= const LocalProcessManager(); processManager ??= const LocalProcessManager();
ProcessResult result; ProcessResult result;
...@@ -58,8 +56,8 @@ Future<bool> killProcess(String pid, {ProcessManager processManager}) async { ...@@ -58,8 +56,8 @@ Future<bool> killProcess(String pid, {ProcessManager processManager}) async {
} }
Stream<RunningProcessInfo> getRunningProcesses({ Stream<RunningProcessInfo> getRunningProcesses({
String processName, String? processName,
ProcessManager processManager, ProcessManager? processManager,
}) { }) {
processManager ??= const LocalProcessManager(); processManager ??= const LocalProcessManager();
if (Platform.isWindows) { if (Platform.isWindows) {
...@@ -69,7 +67,7 @@ Stream<RunningProcessInfo> getRunningProcesses({ ...@@ -69,7 +67,7 @@ Stream<RunningProcessInfo> getRunningProcesses({
} }
@visibleForTesting @visibleForTesting
Stream<RunningProcessInfo> windowsRunningProcesses(String processName) async* { Stream<RunningProcessInfo> windowsRunningProcesses(String? processName) async* {
// PowerShell script to get the command line arguments and create time of // PowerShell script to get the command line arguments and create time of
// a process. // a process.
// See: https://docs.microsoft.com/en-us/windows/desktop/cimwin32prov/win32-process // See: https://docs.microsoft.com/en-us/windows/desktop/cimwin32prov/win32-process
...@@ -107,8 +105,8 @@ Iterable<RunningProcessInfo> processPowershellOutput(String output) sync* { ...@@ -107,8 +105,8 @@ Iterable<RunningProcessInfo> processPowershellOutput(String output) sync* {
const int processIdHeaderSize = 'ProcessId'.length; const int processIdHeaderSize = 'ProcessId'.length;
const int creationDateHeaderStart = processIdHeaderSize + 1; const int creationDateHeaderStart = processIdHeaderSize + 1;
int creationDateHeaderEnd; late int creationDateHeaderEnd;
int commandLineHeaderStart; late int commandLineHeaderStart;
bool inTableBody = false; bool inTableBody = false;
for (final String line in output.split('\n')) { for (final String line in output.split('\n')) {
if (line.startsWith('ProcessId')) { if (line.startsWith('ProcessId')) {
...@@ -160,7 +158,7 @@ Iterable<RunningProcessInfo> processPowershellOutput(String output) sync* { ...@@ -160,7 +158,7 @@ Iterable<RunningProcessInfo> processPowershellOutput(String output) sync* {
@visibleForTesting @visibleForTesting
Stream<RunningProcessInfo> posixRunningProcesses( Stream<RunningProcessInfo> posixRunningProcesses(
String processName, String? processName,
ProcessManager processManager, ProcessManager processManager,
) async* { ) async* {
// Cirrus is missing this in Linux for some reason. // Cirrus is missing this in Linux for some reason.
...@@ -194,7 +192,7 @@ Stream<RunningProcessInfo> posixRunningProcesses( ...@@ -194,7 +192,7 @@ Stream<RunningProcessInfo> posixRunningProcesses(
@visibleForTesting @visibleForTesting
Iterable<RunningProcessInfo> processPsOutput( Iterable<RunningProcessInfo> processPsOutput(
String output, String output,
String processName, String? processName,
) sync* { ) sync* {
if (output == null) { if (output == null) {
return; return;
...@@ -235,7 +233,7 @@ Iterable<RunningProcessInfo> processPsOutput( ...@@ -235,7 +233,7 @@ Iterable<RunningProcessInfo> processPsOutput(
final String rawTime = line.substring(0, 24); final String rawTime = line.substring(0, 24);
final String year = rawTime.substring(20, 24); final String year = rawTime.substring(20, 24);
final String month = months[rawTime.substring(4, 7)]; final String month = months[rawTime.substring(4, 7)]!;
final String day = rawTime.substring(8, 10).replaceFirst(' ', '0'); final String day = rawTime.substring(8, 10).replaceFirst(' ', '0');
final String time = rawTime.substring(11, 19); final String time = rawTime.substring(11, 19);
......
...@@ -41,7 +41,7 @@ class TaskResult { ...@@ -41,7 +41,7 @@ class TaskResult {
List<String> detailFiles = const <String>[], List<String> detailFiles = const <String>[],
}) { }) {
return TaskResult.success( return TaskResult.success(
json.decode(file.readAsStringSync()) as Map<String, dynamic>, json.decode(file.readAsStringSync()) as Map<String, dynamic>?,
benchmarkScoreKeys: benchmarkScoreKeys, benchmarkScoreKeys: benchmarkScoreKeys,
detailFiles: detailFiles, detailFiles: detailFiles,
); );
...@@ -53,14 +53,14 @@ class TaskResult { ...@@ -53,14 +53,14 @@ class TaskResult {
if (success) { if (success) {
final List<String> benchmarkScoreKeys = (json['benchmarkScoreKeys'] as List<dynamic>? ?? <String>[]).cast<String>(); final List<String> benchmarkScoreKeys = (json['benchmarkScoreKeys'] as List<dynamic>? ?? <String>[]).cast<String>();
final List<String> detailFiles = (json['detailFiles'] as List<dynamic>? ?? <String>[]).cast<String>(); final List<String> detailFiles = (json['detailFiles'] as List<dynamic>? ?? <String>[]).cast<String>();
return TaskResult.success(json['data'] as Map<String, dynamic>, return TaskResult.success(json['data'] as Map<String, dynamic>?,
benchmarkScoreKeys: benchmarkScoreKeys, benchmarkScoreKeys: benchmarkScoreKeys,
detailFiles: detailFiles, detailFiles: detailFiles,
message: json['reason'] as String, message: json['reason'] as String?,
); );
} }
return TaskResult.failure(json['reason'] as String); return TaskResult.failure(json['reason'] as String?);
} }
/// Constructs an unsuccessful result. /// Constructs an unsuccessful result.
...@@ -88,7 +88,7 @@ class TaskResult { ...@@ -88,7 +88,7 @@ class TaskResult {
bool get failed => !succeeded; bool get failed => !succeeded;
/// Explains the result in a human-readable format. /// Explains the result in a human-readable format.
final String message; final String? message;
/// Serializes this task result to JSON format. /// Serializes this task result to JSON format.
/// ///
...@@ -124,7 +124,7 @@ class TaskResult { ...@@ -124,7 +124,7 @@ class TaskResult {
} }
@override @override
String toString() => message; String toString() => message ?? '';
} }
class TaskResultCheckProcesses extends TaskResult { class TaskResultCheckProcesses extends TaskResult {
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
...@@ -11,7 +9,6 @@ import 'dart:math' as math; ...@@ -11,7 +9,6 @@ import 'dart:math' as math;
import 'package:flutter_devicelab/common.dart'; import 'package:flutter_devicelab/common.dart';
import 'package:flutter_devicelab/framework/devices.dart'; import 'package:flutter_devicelab/framework/devices.dart';
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'package:process/process.dart'; import 'package:process/process.dart';
import 'package:stack_trace/stack_trace.dart'; import 'package:stack_trace/stack_trace.dart';
...@@ -23,7 +20,7 @@ import 'task_result.dart'; ...@@ -23,7 +20,7 @@ import 'task_result.dart';
String cwd = Directory.current.path; String cwd = Directory.current.path;
/// The local engine to use for [flutter] and [evalFlutter], if any. /// The local engine to use for [flutter] and [evalFlutter], if any.
String get localEngine { String? get localEngine {
// Use two distinct `defaultValue`s to determine whether a 'localEngine' // Use two distinct `defaultValue`s to determine whether a 'localEngine'
// declaration exists in the environment. // declaration exists in the environment.
const bool isDefined = const bool isDefined =
...@@ -34,7 +31,7 @@ String get localEngine { ...@@ -34,7 +31,7 @@ String get localEngine {
/// The local engine source path to use if a local engine is used for [flutter] /// The local engine source path to use if a local engine is used for [flutter]
/// and [evalFlutter]. /// and [evalFlutter].
String get localEngineSrcPath { String? get localEngineSrcPath {
// Use two distinct `defaultValue`s to determine whether a // Use two distinct `defaultValue`s to determine whether a
// 'localEngineSrcPath' declaration exists in the environment. // 'localEngineSrcPath' declaration exists in the environment.
const bool isDefined = const bool isDefined =
...@@ -70,18 +67,18 @@ class HealthCheckResult { ...@@ -70,18 +67,18 @@ class HealthCheckResult {
HealthCheckResult.failure(this.details) : succeeded = false; HealthCheckResult.failure(this.details) : succeeded = false;
HealthCheckResult.error(dynamic error, dynamic stackTrace) HealthCheckResult.error(dynamic error, dynamic stackTrace)
: succeeded = false, : succeeded = false,
details = 'ERROR: $error${'\n$stackTrace' ?? ''}'; details = 'ERROR: $error${stackTrace != null ? '\n$stackTrace' : ''}';
final bool succeeded; final bool succeeded;
final String details; final String? details;
@override @override
String toString() { String toString() {
final StringBuffer buf = StringBuffer(succeeded ? 'succeeded' : 'failed'); final StringBuffer buf = StringBuffer(succeeded ? 'succeeded' : 'failed');
if (details != null && details.trim().isNotEmpty) { if (details != null && details!.trim().isNotEmpty) {
buf.writeln(); buf.writeln();
// Indent details by 4 spaces // Indent details by 4 spaces
for (final String line in details.trim().split('\n')) { for (final String line in details!.trim().split('\n')) {
buf.writeln(' $line'); buf.writeln(' $line');
} }
} }
...@@ -127,7 +124,7 @@ Directory dir(String path) => Directory(path); ...@@ -127,7 +124,7 @@ Directory dir(String path) => Directory(path);
File file(String path) => File(path); File file(String path) => File(path);
void copy(File sourceFile, Directory targetDirectory, {String name}) { void copy(File sourceFile, Directory targetDirectory, {String? name}) {
final File target = file( final File target = file(
path.join(targetDirectory.path, name ?? path.basename(sourceFile.path))); path.join(targetDirectory.path, name ?? path.basename(sourceFile.path)));
target.writeAsBytesSync(sourceFile.readAsBytesSync()); target.writeAsBytesSync(sourceFile.readAsBytesSync());
...@@ -154,7 +151,7 @@ void recursiveCopy(Directory source, Directory target) { ...@@ -154,7 +151,7 @@ void recursiveCopy(Directory source, Directory target) {
} }
FileSystemEntity move(FileSystemEntity whatToMove, FileSystemEntity move(FileSystemEntity whatToMove,
{Directory to, String name}) { {required Directory to, String? name}) {
return whatToMove return whatToMove
.renameSync(path.join(to.path, name ?? path.basename(whatToMove.path))); .renameSync(path.join(to.path, name ?? path.basename(whatToMove.path)));
} }
...@@ -225,9 +222,9 @@ Future<String> getDartVersion() async { ...@@ -225,9 +222,9 @@ Future<String> getDartVersion() async {
return version.replaceAll('"', "'"); return version.replaceAll('"', "'");
} }
Future<String> getCurrentFlutterRepoCommit() { Future<String?> getCurrentFlutterRepoCommit() {
if (!dir('${flutterDirectory.path}/.git').existsSync()) { if (!dir('${flutterDirectory.path}/.git').existsSync()) {
return Future<String>.value(null); return Future<String?>.value(null);
} }
return inDirectory<String>(flutterDirectory, () { return inDirectory<String>(flutterDirectory, () {
...@@ -275,10 +272,10 @@ Future<DateTime> getFlutterRepoCommitTimestamp(String commit) { ...@@ -275,10 +272,10 @@ Future<DateTime> getFlutterRepoCommitTimestamp(String commit) {
/// returned in the form of a [Future] that completes to a [Process] object. /// returned in the form of a [Future] that completes to a [Process] object.
Future<Process> startProcess( Future<Process> startProcess(
String executable, String executable,
List<String> arguments, { List<String>? arguments, {
Map<String, String> environment, Map<String, String>? environment,
bool isBot = true, // set to false to pretend not to be on a bot (e.g. to test user-facing outputs) bool isBot = true, // set to false to pretend not to be on a bot (e.g. to test user-facing outputs)
String workingDirectory, String? workingDirectory,
}) async { }) async {
assert(isBot != null); assert(isBot != null);
final String command = '$executable ${arguments?.join(" ") ?? ""}'; final String command = '$executable ${arguments?.join(" ") ?? ""}';
...@@ -288,7 +285,7 @@ Future<Process> startProcess( ...@@ -288,7 +285,7 @@ Future<Process> startProcess(
newEnvironment['LANG'] = 'en_US.UTF-8'; newEnvironment['LANG'] = 'en_US.UTF-8';
print('\nExecuting: $command in $finalWorkingDirectory with environment $newEnvironment'); print('\nExecuting: $command in $finalWorkingDirectory with environment $newEnvironment');
final Process process = await _processManager.start( final Process process = await _processManager.start(
<String>[executable, ...arguments], <String>[executable, ...?arguments],
environment: newEnvironment, environment: newEnvironment,
workingDirectory: finalWorkingDirectory, workingDirectory: finalWorkingDirectory,
); );
...@@ -324,9 +321,9 @@ Future<void> forceQuitRunningProcesses() async { ...@@ -324,9 +321,9 @@ Future<void> forceQuitRunningProcesses() async {
Future<int> exec( Future<int> exec(
String executable, String executable,
List<String> arguments, { List<String> arguments, {
Map<String, String> environment, Map<String, String>? environment,
bool canFail = false, // as in, whether failures are ok. False means that they are fatal. bool canFail = false, // as in, whether failures are ok. False means that they are fatal.
String workingDirectory, String? workingDirectory,
}) async { }) async {
return _execute( return _execute(
executable, executable,
...@@ -340,11 +337,11 @@ Future<int> exec( ...@@ -340,11 +337,11 @@ Future<int> exec(
Future<int> _execute( Future<int> _execute(
String executable, String executable,
List<String> arguments, { List<String> arguments, {
Map<String, String> environment, Map<String, String>? environment,
bool canFail = false, // as in, whether failures are ok. False means that they are fatal. bool canFail = false, // as in, whether failures are ok. False means that they are fatal.
String workingDirectory, String? workingDirectory,
StringBuffer output, // if not null, the stdout will be written here StringBuffer? output, // if not null, the stdout will be written here
StringBuffer stderr, // if not null, the stderr will be written here StringBuffer? stderr, // if not null, the stderr will be written here
bool printStdout = true, bool printStdout = true,
bool printStderr = true, bool printStderr = true,
}) async { }) async {
...@@ -376,8 +373,8 @@ Future<int> _execute( ...@@ -376,8 +373,8 @@ Future<int> _execute(
/// Returns a future that completes when both out and error streams a closed. /// Returns a future that completes when both out and error streams a closed.
Future<void> forwardStandardStreams( Future<void> forwardStandardStreams(
Process process, { Process process, {
StringBuffer output, StringBuffer? output,
StringBuffer stderr, StringBuffer? stderr,
bool printStdout = true, bool printStdout = true,
bool printStderr = true, bool printStderr = true,
}) { }) {
...@@ -414,10 +411,10 @@ Future<void> forwardStandardStreams( ...@@ -414,10 +411,10 @@ Future<void> forwardStandardStreams(
Future<String> eval( Future<String> eval(
String executable, String executable,
List<String> arguments, { List<String> arguments, {
Map<String, String> environment, Map<String, String>? environment,
bool canFail = false, // as in, whether failures are ok. False means that they are fatal. bool canFail = false, // as in, whether failures are ok. False means that they are fatal.
String workingDirectory, String? workingDirectory,
StringBuffer stderr, // if not null, the stderr will be written here StringBuffer? stderr, // if not null, the stderr will be written here
bool printStdout = true, bool printStdout = true,
bool printStderr = true, bool printStderr = true,
}) async { }) async {
...@@ -457,10 +454,10 @@ List<String> flutterCommandArgs(String command, List<String> options) { ...@@ -457,10 +454,10 @@ List<String> flutterCommandArgs(String command, List<String> options) {
if (command == 'drive' && hostAgent.dumpDirectory != null) ...<String>[ if (command == 'drive' && hostAgent.dumpDirectory != null) ...<String>[
'--screenshot', '--screenshot',
hostAgent.dumpDirectory.path, hostAgent.dumpDirectory!.path,
], ],
if (localEngine != null) ...<String>['--local-engine', localEngine], if (localEngine != null) ...<String>['--local-engine', localEngine!],
if (localEngineSrcPath != null) ...<String>['--local-engine-src-path', localEngineSrcPath], if (localEngineSrcPath != null) ...<String>['--local-engine-src-path', localEngineSrcPath!],
...options, ...options,
]; ];
} }
...@@ -470,7 +467,7 @@ List<String> flutterCommandArgs(String command, List<String> options) { ...@@ -470,7 +467,7 @@ List<String> flutterCommandArgs(String command, List<String> options) {
Future<int> flutter(String command, { Future<int> flutter(String command, {
List<String> options = const <String>[], List<String> options = const <String>[],
bool canFail = false, // as in, whether failures are ok. False means that they are fatal. bool canFail = false, // as in, whether failures are ok. False means that they are fatal.
Map<String, String> environment = const <String, String>{}, Map<String, String>? environment,
}) { }) {
final List<String> args = flutterCommandArgs(command, options); final List<String> args = flutterCommandArgs(command, options);
return exec(path.join(flutterDirectory.path, 'bin', 'flutter'), args, return exec(path.join(flutterDirectory.path, 'bin', 'flutter'), args,
...@@ -493,8 +490,8 @@ Future<Process> startFlutter(String command, { ...@@ -493,8 +490,8 @@ Future<Process> startFlutter(String command, {
Future<String> evalFlutter(String command, { Future<String> evalFlutter(String command, {
List<String> options = const <String>[], List<String> options = const <String>[],
bool canFail = false, // as in, whether failures are ok. False means that they are fatal. bool canFail = false, // as in, whether failures are ok. False means that they are fatal.
Map<String, String> environment, Map<String, String>? environment,
StringBuffer stderr, // if not null, the stderr will be written here. StringBuffer? stderr, // if not null, the stderr will be written here.
}) { }) {
final List<String> args = flutterCommandArgs(command, options); final List<String> args = flutterCommandArgs(command, options);
return eval(path.join(flutterDirectory.path, 'bin', 'flutter'), args, return eval(path.join(flutterDirectory.path, 'bin', 'flutter'), args,
...@@ -521,7 +518,7 @@ Future<int> dart(List<String> args) => exec(dartBin, <String>['--disable-dart-de ...@@ -521,7 +518,7 @@ Future<int> dart(List<String> args) => exec(dartBin, <String>['--disable-dart-de
/// Returns a future that completes with a path suitable for JAVA_HOME /// Returns a future that completes with a path suitable for JAVA_HOME
/// or with null, if Java cannot be found. /// or with null, if Java cannot be found.
Future<String> findJavaHome() async { Future<String?> findJavaHome() async {
if (_javaHome == null) { if (_javaHome == null) {
final Iterable<String> hits = grep( final Iterable<String> hits = grep(
'Java binary at: ', 'Java binary at: ',
...@@ -537,7 +534,7 @@ Future<String> findJavaHome() async { ...@@ -537,7 +534,7 @@ Future<String> findJavaHome() async {
} }
return _javaHome; return _javaHome;
} }
String _javaHome; String? _javaHome;
Future<T> inDirectory<T>(dynamic directory, Future<T> Function() action) async { Future<T> inDirectory<T>(dynamic directory, Future<T> Function() action) async {
final String previousCwd = cwd; final String previousCwd = cwd;
...@@ -568,12 +565,12 @@ void cd(dynamic directory) { ...@@ -568,12 +565,12 @@ void cd(dynamic directory) {
Directory get flutterDirectory => Directory.current.parent.parent; Directory get flutterDirectory => Directory.current.parent.parent;
String requireEnvVar(String name) { String requireEnvVar(String name) {
final String value = Platform.environment[name]; final String? value = Platform.environment[name];
if (value == null) if (value == null)
fail('$name environment variable is missing. Quitting.'); fail('$name environment variable is missing. Quitting.');
return value; return value!;
} }
T requireConfigProperty<T>(Map<String, dynamic> map, String propertyName) { T requireConfigProperty<T>(Map<String, dynamic> map, String propertyName) {
...@@ -637,7 +634,7 @@ void checkNotNull(Object o1, ...@@ -637,7 +634,7 @@ void checkNotNull(Object o1,
} }
/// Splits [from] into lines and selects those that contain [pattern]. /// Splits [from] into lines and selects those that contain [pattern].
Iterable<String> grep(Pattern pattern, {@required String from}) { Iterable<String> grep(Pattern pattern, {required String from}) {
return from.split('\n').where((String line) { return from.split('\n').where((String line) {
return line.contains(pattern); return line.contains(pattern);
}); });
...@@ -675,8 +672,8 @@ final RegExp _obsUriRegExp = RegExp(r'((http|//)[a-zA-Z0-9:/=_\-\.\[\]]+)'); ...@@ -675,8 +672,8 @@ final RegExp _obsUriRegExp = RegExp(r'((http|//)[a-zA-Z0-9:/=_\-\.\[\]]+)');
/// ///
/// The `prefix`, if specified, is a regular expression pattern and must not contain groups. /// The `prefix`, if specified, is a regular expression pattern and must not contain groups.
/// `prefix` defaults to the RegExp: `An Observatory debugger .* is available at: `. /// `prefix` defaults to the RegExp: `An Observatory debugger .* is available at: `.
int parseServicePort(String line, { int? parseServicePort(String line, {
Pattern prefix, Pattern? prefix,
}) { }) {
prefix ??= _obsRegExp; prefix ??= _obsRegExp;
final Iterable<Match> matchesIter = prefix.allMatches(line); final Iterable<Match> matchesIter = prefix.allMatches(line);
...@@ -686,15 +683,15 @@ int parseServicePort(String line, { ...@@ -686,15 +683,15 @@ int parseServicePort(String line, {
final Match prefixMatch = matchesIter.first; final Match prefixMatch = matchesIter.first;
final List<Match> matches = final List<Match> matches =
_obsPortRegExp.allMatches(line, prefixMatch.end).toList(); _obsPortRegExp.allMatches(line, prefixMatch.end).toList();
return matches.isEmpty ? null : int.parse(matches[0].group(2)); return matches.isEmpty ? null : int.parse(matches[0].group(2)!);
} }
/// Tries to extract a URL from the string. /// Tries to extract a URL from the string.
/// ///
/// The `prefix`, if specified, is a regular expression pattern and must not contain groups. /// The `prefix`, if specified, is a regular expression pattern and must not contain groups.
/// `prefix` defaults to the RegExp: `An Observatory debugger .* is available at: `. /// `prefix` defaults to the RegExp: `An Observatory debugger .* is available at: `.
Uri parseServiceUri(String line, { Uri? parseServiceUri(String line, {
Pattern prefix, Pattern? prefix,
}) { }) {
prefix ??= _obsRegExp; prefix ??= _obsRegExp;
final Iterable<Match> matchesIter = prefix.allMatches(line); final Iterable<Match> matchesIter = prefix.allMatches(line);
...@@ -704,7 +701,7 @@ Uri parseServiceUri(String line, { ...@@ -704,7 +701,7 @@ Uri parseServiceUri(String line, {
final Match prefixMatch = matchesIter.first; final Match prefixMatch = matchesIter.first;
final List<Match> matches = final List<Match> matches =
_obsUriRegExp.allMatches(line, prefixMatch.end).toList(); _obsUriRegExp.allMatches(line, prefixMatch.end).toList();
return matches.isEmpty ? null : Uri.parse(matches[0].group(0)); return matches.isEmpty ? null : Uri.parse(matches[0].group(0)!);
} }
/// Checks that the file exists, otherwise throws a [FileSystemException]. /// Checks that the file exists, otherwise throws a [FileSystemException].
...@@ -771,7 +768,7 @@ void checkFileContains(List<Pattern> patterns, String filePath) { ...@@ -771,7 +768,7 @@ void checkFileContains(List<Pattern> patterns, String filePath) {
/// ///
/// Removes the directory [path], then clones the git repository /// Removes the directory [path], then clones the git repository
/// specified by [repo] to the directory [path]. /// specified by [repo] to the directory [path].
Future<int> gitClone({String path, String repo}) async { Future<int> gitClone({required String path, required String repo}) async {
rmTree(Directory(path)); rmTree(Directory(path));
await Directory(path).create(recursive: true); await Directory(path).create(recursive: true);
...@@ -792,7 +789,7 @@ Future<int> gitClone({String path, String repo}) async { ...@@ -792,7 +789,7 @@ Future<int> gitClone({String path, String repo}) async {
/// Waits a constant duration of [delayDuration] between every retry attempt. /// Waits a constant duration of [delayDuration] between every retry attempt.
Future<T> retry<T>( Future<T> retry<T>(
FutureOr<T> Function() fn, { FutureOr<T> Function() fn, {
FutureOr<bool> Function(Exception) retryIf, FutureOr<bool> Function(Exception)? retryIf,
int maxAttempts = 5, int maxAttempts = 5,
Duration delayDuration = const Duration(seconds: 3), Duration delayDuration = const Duration(seconds: 3),
}) async { }) async {
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:io'; import 'dart:io';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:io'; import 'dart:io';
import 'package:args/args.dart'; import 'package:args/args.dart';
...@@ -18,10 +16,9 @@ import '../framework/utils.dart'; ...@@ -18,10 +16,9 @@ import '../framework/utils.dart';
abstract class BuildTestTask { abstract class BuildTestTask {
BuildTestTask(this.args, {this.workingDirectory, this.runFlutterClean = true,}) { BuildTestTask(this.args, {this.workingDirectory, this.runFlutterClean = true,}) {
final ArgResults argResults = argParser.parse(args); final ArgResults argResults = argParser.parse(args);
applicationBinaryPath = argResults[kApplicationBinaryPathOption] as String; applicationBinaryPath = argResults[kApplicationBinaryPathOption] as String?;
buildOnly = argResults[kBuildOnlyFlag] as bool; buildOnly = argResults[kBuildOnlyFlag] as bool;
testOnly = argResults[kTestOnlyFlag] as bool; testOnly = argResults[kTestOnlyFlag] as bool;
} }
static const String kApplicationBinaryPathOption = 'application-binary-path'; static const String kApplicationBinaryPathOption = 'application-binary-path';
...@@ -48,10 +45,10 @@ abstract class BuildTestTask { ...@@ -48,10 +45,10 @@ abstract class BuildTestTask {
/// Path to a built application to use in [test]. /// Path to a built application to use in [test].
/// ///
/// If not given, will default to child's expected location. /// If not given, will default to child's expected location.
String applicationBinaryPath; String? applicationBinaryPath;
/// Where the test artifacts are stored, such as performance results. /// Where the test artifacts are stored, such as performance results.
final Directory workingDirectory; final Directory? workingDirectory;
/// Run Flutter build to create [applicationBinaryPath]. /// Run Flutter build to create [applicationBinaryPath].
Future<void> build() async { Future<void> build() async {
...@@ -93,7 +90,7 @@ abstract class BuildTestTask { ...@@ -93,7 +90,7 @@ abstract class BuildTestTask {
/// ///
/// Tasks can override to support default values. Otherwise, it will default /// Tasks can override to support default values. Otherwise, it will default
/// to needing to be passed as an argument in the test runner. /// to needing to be passed as an argument in the test runner.
String getApplicationBinaryPath() => applicationBinaryPath; String? getApplicationBinaryPath() => applicationBinaryPath;
/// Run this task. /// Run this task.
/// ///
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
...@@ -15,8 +13,8 @@ import 'package:flutter_devicelab/framework/utils.dart'; ...@@ -15,8 +13,8 @@ import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
TaskFunction dartPluginRegistryTest({ TaskFunction dartPluginRegistryTest({
String deviceIdOverride, String? deviceIdOverride,
Map<String, String> environment, Map<String, String>? environment,
}) { }) {
final Directory tempDir = Directory.systemTemp final Directory tempDir = Directory.systemTemp
.createTempSync('flutter_devicelab_dart_plugin_test.'); .createTempSync('flutter_devicelab_dart_plugin_test.');
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'dart:math' as math; import 'dart:math' as math;
...@@ -55,9 +53,9 @@ class GalleryTransitionTest { ...@@ -55,9 +53,9 @@ class GalleryTransitionTest {
final bool needFullTimeline; final bool needFullTimeline;
final String testFile; final String testFile;
final String timelineSummaryFile; final String timelineSummaryFile;
final String timelineTraceFile; final String? timelineTraceFile;
final String transitionDurationFile; final String? transitionDurationFile;
final String driverFile; final String? driverFile;
Future<TaskResult> call() async { Future<TaskResult> call() async {
final Device device = await devices.workingDevice; final Device device = await devices.workingDevice;
...@@ -65,7 +63,7 @@ class GalleryTransitionTest { ...@@ -65,7 +63,7 @@ class GalleryTransitionTest {
final String deviceId = device.deviceId; final String deviceId = device.deviceId;
final Directory galleryDirectory = dir('${flutterDirectory.path}/dev/integration_tests/flutter_gallery'); final Directory galleryDirectory = dir('${flutterDirectory.path}/dev/integration_tests/flutter_gallery');
await inDirectory<void>(galleryDirectory, () async { await inDirectory<void>(galleryDirectory, () async {
String applicationBinaryPath; String? applicationBinaryPath;
if (deviceOperatingSystem == DeviceOperatingSystem.android) { if (deviceOperatingSystem == DeviceOperatingSystem.android) {
section('BUILDING APPLICATION'); section('BUILDING APPLICATION');
await flutter( await flutter(
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
...@@ -20,7 +18,7 @@ final Directory flutterGalleryDir = dir(path.join(flutterDirectory.path, 'dev/in ...@@ -20,7 +18,7 @@ final Directory flutterGalleryDir = dir(path.join(flutterDirectory.path, 'dev/in
const String kSourceLine = 'fontSize: (orientation == Orientation.portrait) ? 32.0 : 24.0'; const String kSourceLine = 'fontSize: (orientation == Orientation.portrait) ? 32.0 : 24.0';
const String kReplacementLine = 'fontSize: (orientation == Orientation.portrait) ? 34.0 : 24.0'; const String kReplacementLine = 'fontSize: (orientation == Orientation.portrait) ? 34.0 : 24.0';
TaskFunction createHotModeTest({String deviceIdOverride, Map<String, String> environment}) { TaskFunction createHotModeTest({String? deviceIdOverride, Map<String, String>? environment}) {
// This file is modified during the test and needs to be restored at the end. // This file is modified during the test and needs to be restored at the end.
final File flutterFrameworkSource = file(path.join( final File flutterFrameworkSource = file(path.join(
flutterDirectory.path, 'packages/flutter/lib/src/widgets/framework.dart', flutterDirectory.path, 'packages/flutter/lib/src/widgets/framework.dart',
...@@ -35,13 +33,13 @@ TaskFunction createHotModeTest({String deviceIdOverride, Map<String, String> env ...@@ -35,13 +33,13 @@ TaskFunction createHotModeTest({String deviceIdOverride, Map<String, String> env
final File benchmarkFile = file(path.join(_editedFlutterGalleryDir.path, 'hot_benchmark.json')); final File benchmarkFile = file(path.join(_editedFlutterGalleryDir.path, 'hot_benchmark.json'));
rm(benchmarkFile); rm(benchmarkFile);
final List<String> options = <String>[ final List<String> options = <String>[
'--hot', '-d', deviceIdOverride, '--benchmark', '--resident', '--no-android-gradle-daemon', '--no-publish-port', '--verbose', '--hot', '-d', deviceIdOverride!, '--benchmark', '--resident', '--no-android-gradle-daemon', '--no-publish-port', '--verbose',
]; ];
int hotReloadCount = 0; int hotReloadCount = 0;
Map<String, dynamic> smallReloadData; late Map<String, dynamic> smallReloadData;
Map<String, dynamic> mediumReloadData; late Map<String, dynamic> mediumReloadData;
Map<String, dynamic> largeReloadData; late Map<String, dynamic> largeReloadData;
Map<String, dynamic> freshRestartReloadsData; late Map<String, dynamic> freshRestartReloadsData;
await inDirectory<void>(flutterDirectory, () async { await inDirectory<void>(flutterDirectory, () async {
...@@ -215,9 +213,9 @@ TaskFunction createHotModeTest({String deviceIdOverride, Map<String, String> env ...@@ -215,9 +213,9 @@ TaskFunction createHotModeTest({String deviceIdOverride, Map<String, String> env
}; };
} }
Future<Map<String, Object>> captureReloadData( Future<Map<String, dynamic>> captureReloadData(
List<String> options, List<String> options,
Map<String, String> environment, Map<String, String>? environment,
File benchmarkFile, File benchmarkFile,
void Function(String, Process) onLine, void Function(String, Process) onLine,
) async { ) async {
...@@ -247,7 +245,7 @@ Future<Map<String, Object>> captureReloadData( ...@@ -247,7 +245,7 @@ Future<Map<String, Object>> captureReloadData(
await Future.wait<void>(<Future<void>>[stdoutDone.future, stderrDone.future]); await Future.wait<void>(<Future<void>>[stdoutDone.future, stderrDone.future]);
await process.exitCode; await process.exitCode;
final Map<String, dynamic> result = json.decode(benchmarkFile.readAsStringSync()) as Map<String, dynamic>; final Map<String, Object> result = json.decode(benchmarkFile.readAsStringSync()) as Map<String, Object>;
benchmarkFile.deleteSync(); benchmarkFile.deleteSync();
return result; return result;
} }
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import '../framework/devices.dart'; import '../framework/devices.dart';
import '../framework/framework.dart'; import '../framework/framework.dart';
import '../framework/task_result.dart'; import '../framework/task_result.dart';
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:io'; import 'dart:io';
import 'package:flutter_devicelab/tasks/perf_tests.dart'; import 'package:flutter_devicelab/tasks/perf_tests.dart';
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:async'; import 'dart:async';
import 'dart:convert' show LineSplitter, json, utf8; import 'dart:convert' show LineSplitter, json, utf8;
import 'dart:io'; import 'dart:io';
...@@ -516,7 +514,7 @@ class StartupTest { ...@@ -516,7 +514,7 @@ class StartupTest {
final List<Map<String, dynamic>> results = <Map<String, dynamic>>[]; final List<Map<String, dynamic>> results = <Map<String, dynamic>>[];
section('Building application'); section('Building application');
String applicationBinaryPath; String? applicationBinaryPath;
switch (deviceOperatingSystem) { switch (deviceOperatingSystem) {
case DeviceOperatingSystem.android: case DeviceOperatingSystem.android:
await flutter('build', options: <String>[ await flutter('build', options: <String>[
...@@ -586,7 +584,7 @@ class StartupTest { ...@@ -586,7 +584,7 @@ class StartupTest {
'-d', '-d',
device.deviceId, device.deviceId,
'--out', '--out',
hostAgent.dumpDirectory hostAgent.dumpDirectory!
.childFile('screenshot_startup_failure_$currentFailures.png') .childFile('screenshot_startup_failure_$currentFailures.png')
.path, .path,
], ],
...@@ -630,7 +628,7 @@ class DevtoolsStartupTest { ...@@ -630,7 +628,7 @@ class DevtoolsStartupTest {
final Device device = await devices.workingDevice; final Device device = await devices.workingDevice;
section('Building application'); section('Building application');
String applicationBinaryPath; String? applicationBinaryPath;
switch (deviceOperatingSystem) { switch (deviceOperatingSystem) {
case DeviceOperatingSystem.android: case DeviceOperatingSystem.android:
await flutter('build', options: <String>[ await flutter('build', options: <String>[
...@@ -733,7 +731,7 @@ class PerfTest { ...@@ -733,7 +731,7 @@ class PerfTest {
this.needsFullTimeline = true, this.needsFullTimeline = true,
this.benchmarkScoreKeys, this.benchmarkScoreKeys,
this.dartDefine = '', this.dartDefine = '',
String resultFilename, String? resultFilename,
}): _resultFilename = resultFilename; }): _resultFilename = resultFilename;
const PerfTest.e2e( const PerfTest.e2e(
...@@ -753,12 +751,12 @@ class PerfTest { ...@@ -753,12 +751,12 @@ class PerfTest {
/// The main entry-point file of the application, as run on the device. /// The main entry-point file of the application, as run on the device.
final String testTarget; final String testTarget;
// The prefix name of the filename such as `<timelineFileName>.timeline_summary.json`. // The prefix name of the filename such as `<timelineFileName>.timeline_summary.json`.
final String timelineFileName; final String? timelineFileName;
String get traceFilename => '$timelineFileName.timeline'; String get traceFilename => '$timelineFileName.timeline';
String get resultFilename => _resultFilename ?? '$timelineFileName.timeline_summary'; String get resultFilename => _resultFilename ?? '$timelineFileName.timeline_summary';
final String _resultFilename; final String? _resultFilename;
/// The test file to run on the host. /// The test file to run on the host.
final String testDriver; final String? testDriver;
/// Whether to collect CPU and GPU metrics. /// Whether to collect CPU and GPU metrics.
final bool measureCpuGpu; final bool measureCpuGpu;
/// Whether to collect memory metrics. /// Whether to collect memory metrics.
...@@ -788,7 +786,7 @@ class PerfTest { ...@@ -788,7 +786,7 @@ class PerfTest {
/// if (measureCpuGpu) 'average_gpu_usage', /// if (measureCpuGpu) 'average_gpu_usage',
/// ] /// ]
/// ``` /// ```
final List<String> benchmarkScoreKeys; final List<String>? benchmarkScoreKeys;
/// Additional flags for `--dart-define` to control the test /// Additional flags for `--dart-define` to control the test
final String dartDefine; final String dartDefine;
...@@ -800,8 +798,8 @@ class PerfTest { ...@@ -800,8 +798,8 @@ class PerfTest {
@protected @protected
Future<TaskResult> internalRun({ Future<TaskResult> internalRun({
bool cacheSkSL = false, bool cacheSkSL = false,
String existingApp, String? existingApp,
String writeSkslFileName, String? writeSkslFileName,
}) { }) {
return inDirectory<TaskResult>(testDirectory, () async { return inDirectory<TaskResult>(testDirectory, () async {
final Device device = await devices.workingDevice; final Device device = await devices.workingDevice;
...@@ -818,7 +816,7 @@ class PerfTest { ...@@ -818,7 +816,7 @@ class PerfTest {
'--trace-startup', // Enables "endless" timeline event buffering. '--trace-startup', // Enables "endless" timeline event buffering.
'-t', testTarget, '-t', testTarget,
if (testDriver != null) if (testDriver != null)
...<String>['--driver', testDriver], ...<String>['--driver', testDriver!],
if (existingApp != null) if (existingApp != null)
...<String>['--use-existing-app', existingApp], ...<String>['--use-existing-app', existingApp],
if (writeSkslFileName != null) if (writeSkslFileName != null)
...@@ -890,9 +888,9 @@ class PerfTestWithSkSL extends PerfTest { ...@@ -890,9 +888,9 @@ class PerfTestWithSkSL extends PerfTest {
String testTarget, String testTarget,
String timelineFileName, { String timelineFileName, {
bool measureCpuGpu = false, bool measureCpuGpu = false,
String testDriver, String? testDriver,
bool needsFullTimeline = true, bool needsFullTimeline = true,
List<String> benchmarkScoreKeys, List<String>? benchmarkScoreKeys,
}) : super( }) : super(
testDirectory, testDirectory,
testTarget, testTarget,
...@@ -964,7 +962,7 @@ class PerfTestWithSkSL extends PerfTest { ...@@ -964,7 +962,7 @@ class PerfTestWithSkSL extends PerfTest {
); );
} }
Future<String> _runApp({String appBinary, bool cacheSkSL = false, String skslPath}) async { Future<String> _runApp({String? appBinary, bool cacheSkSL = false, String? skslPath}) async {
if (File(_vmserviceFileName).existsSync()) { if (File(_vmserviceFileName).existsSync()) {
File(_vmserviceFileName).deleteSync(); File(_vmserviceFileName).deleteSync();
} }
...@@ -1027,9 +1025,9 @@ class PerfTestWithSkSL extends PerfTest { ...@@ -1027,9 +1025,9 @@ class PerfTestWithSkSL extends PerfTest {
}); });
} }
String _flutterPath; late String _flutterPath;
Device _device; late Device _device;
Process _runProcess; late Process _runProcess;
static const String _kVmserviceOutFileName = 'vmservice.out'; static const String _kVmserviceOutFileName = 'vmservice.out';
} }
...@@ -1073,12 +1071,16 @@ class WebCompileTest { ...@@ -1073,12 +1071,16 @@ class WebCompileTest {
/// ///
/// Run a single web compile test for the app under [directory], and store /// Run a single web compile test for the app under [directory], and store
/// its metrics with prefix [metric]. /// its metrics with prefix [metric].
static Future<Map<String, int>> runSingleBuildTest({String directory, String metric, bool measureBuildTime = false}) { static Future<Map<String, int>> runSingleBuildTest({
required String directory,
required String metric,
bool measureBuildTime = false,
}) {
return inDirectory<Map<String, int>>(directory, () async { return inDirectory<Map<String, int>>(directory, () async {
final Map<String, int> metrics = <String, int>{}; final Map<String, int> metrics = <String, int>{};
await flutter('packages', options: <String>['get']); await flutter('packages', options: <String>['get']);
final Stopwatch watch = measureBuildTime ? Stopwatch() : null; final Stopwatch? watch = measureBuildTime ? Stopwatch() : null;
watch?.start(); watch?.start();
await evalFlutter('build', options: <String>[ await evalFlutter('build', options: <String>[
'web', 'web',
...@@ -1091,7 +1093,7 @@ class WebCompileTest { ...@@ -1091,7 +1093,7 @@ class WebCompileTest {
metrics.addAll(await getSize(outputFileName, metric: metric)); metrics.addAll(await getSize(outputFileName, metric: metric));
if (measureBuildTime) { if (measureBuildTime) {
metrics['${metric}_dart2js_millis'] = watch.elapsedMilliseconds; metrics['${metric}_dart2js_millis'] = watch!.elapsedMilliseconds;
} }
return metrics; return metrics;
...@@ -1099,7 +1101,7 @@ class WebCompileTest { ...@@ -1099,7 +1101,7 @@ class WebCompileTest {
} }
/// Obtains the size and gzipped size of a file given by [fileName]. /// Obtains the size and gzipped size of a file given by [fileName].
static Future<Map<String, int>> getSize(String fileName, {String metric}) async { static Future<Map<String, int>> getSize(String fileName, {required String metric}) async {
final Map<String, int> sizeMetrics = <String, int>{}; final Map<String, int> sizeMetrics = <String, int>{};
final ProcessResult result = await Process.run('du', <String>['-k', fileName]); final ProcessResult result = await Process.run('du', <String>['-k', fileName]);
...@@ -1168,10 +1170,12 @@ class CompileTest { ...@@ -1168,10 +1170,12 @@ class CompileTest {
await flutter('build', options: options); await flutter('build', options: options);
watch.stop(); watch.stop();
final Directory appBuildDirectory = dir(path.join(cwd, 'build/ios/Release-iphoneos')); final Directory appBuildDirectory = dir(path.join(cwd, 'build/ios/Release-iphoneos'));
final Directory appBundle = appBuildDirectory final Directory? appBundle = appBuildDirectory
.listSync() .listSync()
.whereType<Directory>() .whereType<Directory?>()
.singleWhere((Directory directory) => path.extension(directory.path) == '.app', orElse: () => null); .singleWhere((Directory? directory) =>
directory != null && path.extension(directory.path) == '.app',
orElse: () => null);
if (appBundle == null) { if (appBundle == null) {
throw 'Failed to find app bundle in ${appBuildDirectory.path}'; throw 'Failed to find app bundle in ${appBuildDirectory.path}';
} }
...@@ -1226,8 +1230,8 @@ class CompileTest { ...@@ -1226,8 +1230,8 @@ class CompileTest {
} }
static Future<Map<String, dynamic>> _compileDebug({ static Future<Map<String, dynamic>> _compileDebug({
@required bool clean, required bool clean,
@required String metricKey, required String metricKey,
}) async { }) async {
if (clean) { if (clean) {
await flutter('clean'); await flutter('clean');
...@@ -1290,9 +1294,9 @@ class CompileTest { ...@@ -1290,9 +1294,9 @@ class CompileTest {
fileToMetadata[entry.path] = entry; fileToMetadata[entry.path] = entry;
} }
final _UnzipListEntry libflutter = fileToMetadata['lib/armeabi-v7a/libflutter.so']; final _UnzipListEntry libflutter = fileToMetadata['lib/armeabi-v7a/libflutter.so']!;
final _UnzipListEntry libapp = fileToMetadata['lib/armeabi-v7a/libapp.so']; final _UnzipListEntry libapp = fileToMetadata['lib/armeabi-v7a/libapp.so']!;
final _UnzipListEntry license = fileToMetadata['assets/flutter_assets/NOTICES.Z']; final _UnzipListEntry license = fileToMetadata['assets/flutter_assets/NOTICES.Z']!;
return <String, dynamic>{ return <String, dynamic>{
'libflutter_uncompressed_bytes': libflutter.uncompressedSize, 'libflutter_uncompressed_bytes': libflutter.uncompressedSize,
...@@ -1315,9 +1319,9 @@ class MemoryTest { ...@@ -1315,9 +1319,9 @@ class MemoryTest {
/// Completes when the log line specified in the last call to /// Completes when the log line specified in the last call to
/// [prepareForNextMessage] is seen by `adb logcat`. /// [prepareForNextMessage] is seen by `adb logcat`.
Future<void> get receivedNextMessage => _receivedNextMessage?.future; Future<void>? get receivedNextMessage => _receivedNextMessage?.future;
Completer<void> _receivedNextMessage; Completer<void>? _receivedNextMessage;
String _nextMessage; String? _nextMessage;
/// Prepares the [receivedNextMessage] future such that it will complete /// Prepares the [receivedNextMessage] future such that it will complete
/// when `adb logcat` sees a log line with the given `message`. /// when `adb logcat` sees a log line with the given `message`.
...@@ -1328,8 +1332,8 @@ class MemoryTest { ...@@ -1328,8 +1332,8 @@ class MemoryTest {
int get iterationCount => 10; int get iterationCount => 10;
Device get device => _device; Device? get device => _device;
Device _device; Device? _device;
Future<TaskResult> run() { Future<TaskResult> run() {
return inDirectory<TaskResult>(project, () async { return inDirectory<TaskResult>(project, () async {
...@@ -1337,13 +1341,13 @@ class MemoryTest { ...@@ -1337,13 +1341,13 @@ class MemoryTest {
// device.getMemoryStats, etc, aren't implemented for iOS. // device.getMemoryStats, etc, aren't implemented for iOS.
_device = await devices.workingDevice; _device = await devices.workingDevice;
await device.unlock(); await device!.unlock();
await flutter('packages', options: <String>['get']); await flutter('packages', options: <String>['get']);
final StreamSubscription<String> adb = device.logcat.listen( final StreamSubscription<String> adb = device!.logcat.listen(
(String data) { (String data) {
if (data.contains('==== MEMORY BENCHMARK ==== $_nextMessage ====')) if (data.contains('==== MEMORY BENCHMARK ==== $_nextMessage ===='))
_receivedNextMessage.complete(); _receivedNextMessage?.complete();
}, },
); );
...@@ -1356,12 +1360,12 @@ class MemoryTest { ...@@ -1356,12 +1360,12 @@ class MemoryTest {
assert(_endMemory.length == iteration + 1); assert(_endMemory.length == iteration + 1);
assert(_diffMemory.length == iteration + 1); assert(_diffMemory.length == iteration + 1);
print('terminating...'); print('terminating...');
await device.stop(package); await device!.stop(package);
await Future<void>.delayed(const Duration(milliseconds: 10)); await Future<void>.delayed(const Duration(milliseconds: 10));
} }
await adb.cancel(); await adb.cancel();
await flutter('install', options: <String>['--uninstall-only', '-d', device.deviceId]); await flutter('install', options: <String>['--uninstall-only', '-d', device!.deviceId]);
final ListStatistics startMemoryStatistics = ListStatistics(_startMemory); final ListStatistics startMemoryStatistics = ListStatistics(_startMemory);
final ListStatistics endMemoryStatistics = ListStatistics(_endMemory); final ListStatistics endMemoryStatistics = ListStatistics(_endMemory);
...@@ -1392,7 +1396,7 @@ class MemoryTest { ...@@ -1392,7 +1396,7 @@ class MemoryTest {
'--verbose', '--verbose',
'--release', '--release',
'--no-resident', '--no-resident',
'-d', device.deviceId, '-d', device!.deviceId,
test, test,
]); ]);
print('awaiting "ready" message...'); print('awaiting "ready" message...');
...@@ -1411,7 +1415,7 @@ class MemoryTest { ...@@ -1411,7 +1415,7 @@ class MemoryTest {
prepareForNextMessage('DONE'); prepareForNextMessage('DONE');
print('tapping device...'); print('tapping device...');
await device.tap(100, 100); await device!.tap(100, 100);
print('awaiting "done" message...'); print('awaiting "done" message...');
await receivedNextMessage; await receivedNextMessage;
...@@ -1422,23 +1426,23 @@ class MemoryTest { ...@@ -1422,23 +1426,23 @@ class MemoryTest {
final List<int> _endMemory = <int>[]; final List<int> _endMemory = <int>[];
final List<int> _diffMemory = <int>[]; final List<int> _diffMemory = <int>[];
Map<String, dynamic> _startMemoryUsage; Map<String, dynamic>? _startMemoryUsage;
@protected @protected
Future<void> recordStart() async { Future<void> recordStart() async {
assert(_startMemoryUsage == null); assert(_startMemoryUsage == null);
print('snapshotting memory usage...'); print('snapshotting memory usage...');
_startMemoryUsage = await device.getMemoryStats(package); _startMemoryUsage = await device!.getMemoryStats(package);
} }
@protected @protected
Future<void> recordEnd() async { Future<void> recordEnd() async {
assert(_startMemoryUsage != null); assert(_startMemoryUsage != null);
print('snapshotting memory usage...'); print('snapshotting memory usage...');
final Map<String, dynamic> endMemoryUsage = await device.getMemoryStats(package); final Map<String, dynamic> endMemoryUsage = await device!.getMemoryStats(package);
_startMemory.add(_startMemoryUsage['total_kb'] as int); _startMemory.add(_startMemoryUsage!['total_kb'] as int);
_endMemory.add(endMemoryUsage['total_kb'] as int); _endMemory.add(endMemoryUsage['total_kb'] as int);
_diffMemory.add((endMemoryUsage['total_kb'] as int) - (_startMemoryUsage['total_kb'] as int)); _diffMemory.add((endMemoryUsage['total_kb'] as int) - (_startMemoryUsage!['total_kb'] as int));
} }
} }
...@@ -1487,7 +1491,7 @@ class DevToolsMemoryTest { ...@@ -1487,7 +1491,7 @@ class DevToolsMemoryTest {
}); });
} }
Device _device; late Device _device;
static const String _kJsonFileName = 'devtools_memory.json'; static const String _kJsonFileName = 'devtools_memory.json';
} }
...@@ -1505,7 +1509,6 @@ String _reportedDurationTestToString(ReportedDurationTestFlavor flavor) { ...@@ -1505,7 +1509,6 @@ String _reportedDurationTestToString(ReportedDurationTestFlavor flavor) {
case ReportedDurationTestFlavor.release: case ReportedDurationTestFlavor.release:
return 'release'; return 'release';
} }
throw ArgumentError('Unexpected value for enum $flavor');
} }
class ReportedDurationTest { class ReportedDurationTest {
...@@ -1521,8 +1524,8 @@ class ReportedDurationTest { ...@@ -1521,8 +1524,8 @@ class ReportedDurationTest {
int get iterationCount => 10; int get iterationCount => 10;
Device get device => _device; Device? get device => _device;
Device _device; Device? _device;
Future<TaskResult> run() { Future<TaskResult> run() {
return inDirectory<TaskResult>(project, () async { return inDirectory<TaskResult>(project, () async {
...@@ -1530,13 +1533,13 @@ class ReportedDurationTest { ...@@ -1530,13 +1533,13 @@ class ReportedDurationTest {
// device.getMemoryStats, etc, aren't implemented for iOS. // device.getMemoryStats, etc, aren't implemented for iOS.
_device = await devices.workingDevice; _device = await devices.workingDevice;
await device.unlock(); await device!.unlock();
await flutter('packages', options: <String>['get']); await flutter('packages', options: <String>['get']);
final StreamSubscription<String> adb = device.logcat.listen( final StreamSubscription<String> adb = device!.logcat.listen(
(String data) { (String data) {
if (durationPattern.hasMatch(data)) if (durationPattern.hasMatch(data))
durationCompleter.complete(int.parse(durationPattern.firstMatch(data).group(1))); durationCompleter.complete(int.parse(durationPattern.firstMatch(data)!.group(1)!));
}, },
); );
print('launching $project$test on device...'); print('launching $project$test on device...');
...@@ -1546,13 +1549,13 @@ class ReportedDurationTest { ...@@ -1546,13 +1549,13 @@ class ReportedDurationTest {
'--no-fast-start', '--no-fast-start',
'--${_reportedDurationTestToString(flavor)}', '--${_reportedDurationTestToString(flavor)}',
'--no-resident', '--no-resident',
'-d', device.deviceId, '-d', device!.deviceId,
test, test,
]); ]);
final int duration = await durationCompleter.future; final int duration = await durationCompleter.future;
print('terminating...'); print('terminating...');
await device.stop(package); await device!.stop(package);
await adb.cancel(); await adb.cancel();
_device = null; _device = null;
...@@ -1607,9 +1610,9 @@ class _UnzipListEntry { ...@@ -1607,9 +1610,9 @@ class _UnzipListEntry {
} }
_UnzipListEntry._({ _UnzipListEntry._({
@required this.uncompressedSize, required this.uncompressedSize,
@required this.compressedSize, required this.compressedSize,
@required this.path, required this.path,
}) : assert(uncompressedSize != null), }) : assert(uncompressedSize != null),
assert(compressedSize != null), assert(compressedSize != null),
assert(compressedSize <= uncompressedSize), assert(compressedSize <= uncompressedSize),
...@@ -1633,7 +1636,7 @@ Future<File> waitForFile(String path) async { ...@@ -1633,7 +1636,7 @@ Future<File> waitForFile(String path) async {
throw StateError('Did not find vmservice out file after 400 seconds'); throw StateError('Did not find vmservice out file after 400 seconds');
} }
String _findIosAppInBuildDirectory(String searchDirectory) { String? _findIosAppInBuildDirectory(String searchDirectory) {
for (final FileSystemEntity entity in Directory(searchDirectory).listSync()) { for (final FileSystemEntity entity in Directory(searchDirectory).listSync()) {
if (entity.path.endsWith('.app')) { if (entity.path.endsWith('.app')) {
return entity.path; return entity.path;
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:io' show Process, Directory; import 'dart:io' show Process, Directory;
import 'package:flutter_devicelab/framework/devices.dart' as adb; import 'package:flutter_devicelab/framework/devices.dart' as adb;
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:io'; import 'dart:io';
import 'package:flutter_devicelab/framework/framework.dart'; import 'package:flutter_devicelab/framework/framework.dart';
...@@ -31,8 +29,8 @@ class PluginTest { ...@@ -31,8 +29,8 @@ class PluginTest {
final String buildTarget; final String buildTarget;
final List<String> options; final List<String> options;
final Map<String, String> pluginCreateEnvironment; final Map<String, String>? pluginCreateEnvironment;
final Map<String, String> appCreateEnvironment; final Map<String, String>? appCreateEnvironment;
Future<TaskResult> call() async { Future<TaskResult> call() async {
final Directory tempDir = final Directory tempDir =
...@@ -77,7 +75,7 @@ class _FlutterProject { ...@@ -77,7 +75,7 @@ class _FlutterProject {
String get rootPath => path.join(parent.path, name); String get rootPath => path.join(parent.path, name);
Future<void> addPlugin(String plugin, {String pluginPath}) async { Future<void> addPlugin(String plugin, {String? pluginPath}) async {
final File pubspec = File(path.join(rootPath, 'pubspec.yaml')); final File pubspec = File(path.join(rootPath, 'pubspec.yaml'));
String content = await pubspec.readAsString(); String content = await pubspec.readAsString();
final String dependency = final String dependency =
...@@ -100,9 +98,9 @@ class _FlutterProject { ...@@ -100,9 +98,9 @@ class _FlutterProject {
List<String> options, List<String> options,
String target, String target,
{ {
String name, required String name,
String template, required String template,
Map<String, String> environment, Map<String, String>? environment,
}) async { }) async {
await inDirectory(directory, () async { await inDirectory(directory, () async {
await flutter( await flutter(
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:async'; import 'dart:async';
import 'dart:convert' show json; import 'dart:convert' show json;
import 'dart:io' as io; import 'dart:io' as io;
...@@ -13,7 +11,6 @@ import 'package:flutter_devicelab/framework/browser.dart'; ...@@ -13,7 +11,6 @@ import 'package:flutter_devicelab/framework/browser.dart';
import 'package:flutter_devicelab/framework/task_result.dart'; import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart'; import 'package:flutter_devicelab/framework/utils.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'package:shelf/shelf.dart'; import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as shelf_io; import 'package:shelf/shelf_io.dart' as shelf_io;
...@@ -23,7 +20,7 @@ import 'package:shelf_static/shelf_static.dart'; ...@@ -23,7 +20,7 @@ import 'package:shelf_static/shelf_static.dart';
const int benchmarkServerPort = 9999; const int benchmarkServerPort = 9999;
const int chromeDebugPort = 10000; const int chromeDebugPort = 10000;
Future<TaskResult> runWebBenchmark({ @required bool useCanvasKit }) async { Future<TaskResult> runWebBenchmark({ required bool useCanvasKit }) async {
// Reduce logging level. Otherwise, package:webkit_inspection_protocol is way too spammy. // Reduce logging level. Otherwise, package:webkit_inspection_protocol is way too spammy.
Logger.root.level = Level.INFO; Logger.root.level = Level.INFO;
final String macrobenchmarksDirectory = path.join(flutterDirectory.path, 'dev', 'benchmarks', 'macrobenchmarks'); final String macrobenchmarksDirectory = path.join(flutterDirectory.path, 'dev', 'benchmarks', 'macrobenchmarks');
...@@ -38,17 +35,17 @@ Future<TaskResult> runWebBenchmark({ @required bool useCanvasKit }) async { ...@@ -38,17 +35,17 @@ Future<TaskResult> runWebBenchmark({ @required bool useCanvasKit }) async {
]); ]);
final Completer<List<Map<String, dynamic>>> profileData = Completer<List<Map<String, dynamic>>>(); final Completer<List<Map<String, dynamic>>> profileData = Completer<List<Map<String, dynamic>>>();
final List<Map<String, dynamic>> collectedProfiles = <Map<String, dynamic>>[]; final List<Map<String, dynamic>> collectedProfiles = <Map<String, dynamic>>[];
List<String> benchmarks; List<String>? benchmarks;
Iterator<String> benchmarkIterator; late Iterator<String> benchmarkIterator;
// This future fixes a race condition between the web-page loading and // This future fixes a race condition between the web-page loading and
// asking to run a benchmark, and us connecting to Chrome's DevTools port. // asking to run a benchmark, and us connecting to Chrome's DevTools port.
// Sometime one wins. Other times, the other wins. // Sometime one wins. Other times, the other wins.
Future<Chrome> whenChromeIsReady; Future<Chrome>? whenChromeIsReady;
Chrome chrome; Chrome? chrome;
io.HttpServer server; late io.HttpServer server;
Cascade cascade = Cascade(); Cascade cascade = Cascade();
List<Map<String, dynamic>> latestPerformanceTrace; List<Map<String, dynamic>>? latestPerformanceTrace;
cascade = cascade.add((Request request) async { cascade = cascade.add((Request request) async {
try { try {
chrome ??= await whenChromeIsReady; chrome ??= await whenChromeIsReady;
...@@ -66,7 +63,7 @@ Future<TaskResult> runWebBenchmark({ @required bool useCanvasKit }) async { ...@@ -66,7 +63,7 @@ Future<TaskResult> runWebBenchmark({ @required bool useCanvasKit }) async {
// Trace data is null when the benchmark is not frame-based, such as RawRecorder. // Trace data is null when the benchmark is not frame-based, such as RawRecorder.
if (latestPerformanceTrace != null) { if (latestPerformanceTrace != null) {
final BlinkTraceSummary traceSummary = BlinkTraceSummary.fromJson(latestPerformanceTrace); final BlinkTraceSummary traceSummary = BlinkTraceSummary.fromJson(latestPerformanceTrace!)!;
profile['totalUiFrame.average'] = traceSummary.averageTotalUIFrameTime.inMicroseconds; profile['totalUiFrame.average'] = traceSummary.averageTotalUIFrameTime.inMicroseconds;
profile['scoreKeys'] ??= <dynamic>[]; // using dynamic for consistency with JSON profile['scoreKeys'] ??= <dynamic>[]; // using dynamic for consistency with JSON
(profile['scoreKeys'] as List<dynamic>).add('totalUiFrame.average'); (profile['scoreKeys'] as List<dynamic>).add('totalUiFrame.average');
...@@ -76,10 +73,10 @@ Future<TaskResult> runWebBenchmark({ @required bool useCanvasKit }) async { ...@@ -76,10 +73,10 @@ Future<TaskResult> runWebBenchmark({ @required bool useCanvasKit }) async {
return Response.ok('Profile received'); return Response.ok('Profile received');
} else if (request.requestedUri.path.endsWith('/start-performance-tracing')) { } else if (request.requestedUri.path.endsWith('/start-performance-tracing')) {
latestPerformanceTrace = null; latestPerformanceTrace = null;
await chrome.beginRecordingPerformance(request.requestedUri.queryParameters['label']); await chrome!.beginRecordingPerformance(request.requestedUri.queryParameters['label']!);
return Response.ok('Started performance tracing'); return Response.ok('Started performance tracing');
} else if (request.requestedUri.path.endsWith('/stop-performance-tracing')) { } else if (request.requestedUri.path.endsWith('/stop-performance-tracing')) {
latestPerformanceTrace = await chrome.endRecordingPerformance(); latestPerformanceTrace = await chrome!.endRecordingPerformance();
return Response.ok('Stopped performance tracing'); return Response.ok('Stopped performance tracing');
} else if (request.requestedUri.path.endsWith('/on-error')) { } else if (request.requestedUri.path.endsWith('/on-error')) {
final Map<String, dynamic> errorDetails = json.decode(await request.readAsString()) as Map<String, dynamic>; final Map<String, dynamic> errorDetails = json.decode(await request.readAsString()) as Map<String, dynamic>;
...@@ -90,7 +87,7 @@ Future<TaskResult> runWebBenchmark({ @required bool useCanvasKit }) async { ...@@ -90,7 +87,7 @@ Future<TaskResult> runWebBenchmark({ @required bool useCanvasKit }) async {
} else if (request.requestedUri.path.endsWith('/next-benchmark')) { } else if (request.requestedUri.path.endsWith('/next-benchmark')) {
if (benchmarks == null) { if (benchmarks == null) {
benchmarks = (json.decode(await request.readAsString()) as List<dynamic>).cast<String>(); benchmarks = (json.decode(await request.readAsString()) as List<dynamic>).cast<String>();
benchmarkIterator = benchmarks.iterator; benchmarkIterator = benchmarks!.iterator;
} }
if (benchmarkIterator.moveNext()) { if (benchmarkIterator.moveNext()) {
final String nextBenchmark = benchmarkIterator.current; final String nextBenchmark = benchmarkIterator.current;
...@@ -186,7 +183,7 @@ Future<TaskResult> runWebBenchmark({ @required bool useCanvasKit }) async { ...@@ -186,7 +183,7 @@ Future<TaskResult> runWebBenchmark({ @required bool useCanvasKit }) async {
} }
return TaskResult.success(taskResult, benchmarkScoreKeys: benchmarkScoreKeys); return TaskResult.success(taskResult, benchmarkScoreKeys: benchmarkScoreKeys);
} finally { } finally {
unawaited(server?.close()); unawaited(server.close());
chrome?.stop(); chrome?.stop();
} }
}); });
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment