Migrate devicelab tests and test runners to null safety. (#85999)

* Migrate devicelab tests and test runners to null safety.
diff --git a/dev/devicelab/bin/run.dart b/dev/devicelab/bin/run.dart
index dbc7c14..2967b66 100644
--- a/dev/devicelab/bin/run.dart
+++ b/dev/devicelab/bin/run.dart
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// @dart = 2.8
-
 import 'dart:convert';
 import 'dart:io';
 
@@ -15,43 +13,43 @@
 import 'package:flutter_devicelab/framework/utils.dart';
 import 'package:path/path.dart' as path;
 
-ArgResults args;
+late ArgResults args;
 
 List<String> _taskNames = <String>[];
 
 /// The device-id to run test on.
-String deviceId;
+String? deviceId;
 
 /// The git branch being tested on.
-String gitBranch;
+String? gitBranch;
 
 /// The build of the local engine to use.
 ///
 /// Required for A/B test mode.
-String localEngine;
+String? localEngine;
 
 /// The path to the engine "src/" directory.
-String localEngineSrcPath;
+String? localEngineSrcPath;
 
 /// Name of the LUCI builder this test is currently running on.
 ///
 /// This is only passed on CI runs for Cocoon to be able to uniquely identify
 /// this test run.
-String luciBuilder;
+String? luciBuilder;
 
 /// Whether to exit on first test failure.
-bool exitOnFirstTestFailure;
+bool exitOnFirstTestFailure = false;
 
 /// Path to write test results to.
-String resultsPath;
+String? resultsPath;
 
 /// File containing a service account token.
 ///
 /// If passed, the test run results will be uploaded to Flutter infrastructure.
-String serviceAccountTokenFile;
+String? serviceAccountTokenFile;
 
 /// Suppresses standard output, prints only standard error output.
-bool silent;
+bool silent = false;
 
 /// Runs tasks.
 ///
@@ -68,15 +66,15 @@
     return;
   }
 
-  deviceId = args['device-id'] as String;
-  exitOnFirstTestFailure = args['exit'] as bool;
-  gitBranch = args['git-branch'] as String;
-  localEngine = args['local-engine'] as String;
-  localEngineSrcPath = args['local-engine-src-path'] as String;
-  luciBuilder = args['luci-builder'] as String;
-  resultsPath = args['results-file'] as String;
-  serviceAccountTokenFile = args['service-account-token-file'] as String;
-  silent = args['silent'] as bool;
+  deviceId = args['device-id'] as String?;
+  exitOnFirstTestFailure = (args['exit'] as bool?) ?? false;
+  gitBranch = args['git-branch'] as String?;
+  localEngine = args['local-engine'] as String?;
+  localEngineSrcPath = args['local-engine-src-path'] as String?;
+  luciBuilder = args['luci-builder'] as String?;
+  resultsPath = args['results-file'] as String?;
+  serviceAccountTokenFile = args['service-account-token-file'] as String?;
+  silent = (args['silent'] as bool?) ?? false;
 
   if (!args.wasParsed('task')) {
     if (args.wasParsed('stage') || args.wasParsed('all')) {
@@ -137,7 +135,7 @@
 
   print('$taskName A/B test. Will run $runsPerTest times.');
 
-  final ABTest abTest = ABTest(localEngine, taskName);
+  final ABTest abTest = ABTest(localEngine!, taskName);
   for (int i = 1; i <= runsPerTest; i++) {
     section('Run #$i');
 
@@ -177,17 +175,17 @@
 
     abTest.addBResult(localEngineResult);
 
-    if (!silent && i < runsPerTest) {
+    if (silent != true && i < runsPerTest) {
       section('A/B results so far');
       print(abTest.printSummary());
     }
   }
   abTest.finalize();
 
-  final File jsonFile = _uniqueFile(args['ab-result-file'] as String ?? 'ABresults#.json');
+  final File jsonFile = _uniqueFile(args['ab-result-file'] as String? ?? 'ABresults#.json');
   jsonFile.writeAsStringSync(const JsonEncoder.withIndent('  ').convert(abTest.jsonMap));
 
-  if (!silent) {
+  if (silent != true) {
     section('Raw results');
     print(abTest.rawResults());
   }
@@ -214,9 +212,9 @@
 }
 
 void addTasks({
-  List<ManifestTask> tasks,
-  ArgResults args,
-  List<String> taskNames,
+  required List<ManifestTask> tasks,
+  required ArgResults args,
+  required List<String> taskNames,
 }) {
   if (args.wasParsed('continue-from')) {
     final int index = tasks.indexWhere((ManifestTask task) => task.name == args['continue-from']);
@@ -286,7 +284,7 @@
           'produces a report containing averages, noise, and the speed-up\n'
           'between the two engines. --local-engine is required when running\n'
           'an A/B test.',
-    callback: (String value) {
+    callback: (String? value) {
       if (value != null && int.tryParse(value) == null) {
         throw ArgParserException('Option --ab must be a number, but was "$value".');
       }
diff --git a/dev/devicelab/bin/summarize.dart b/dev/devicelab/bin/summarize.dart
index 53075e1..2a3b28a 100644
--- a/dev/devicelab/bin/summarize.dart
+++ b/dev/devicelab/bin/summarize.dart
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// @dart = 2.8
-
 import 'dart:convert';
 import 'dart:io';
 
diff --git a/dev/devicelab/bin/test_runner.dart b/dev/devicelab/bin/test_runner.dart
index ac2b371..fb80dad 100644
--- a/dev/devicelab/bin/test_runner.dart
+++ b/dev/devicelab/bin/test_runner.dart
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// @dart = 2.8
-
 import 'dart:io';
 
 import 'package:args/command_runner.dart';
diff --git a/dev/devicelab/test/ab_test.dart b/dev/devicelab/test/ab_test.dart
index 5e22349..27e92fd 100644
--- a/dev/devicelab/test/ab_test.dart
+++ b/dev/devicelab/test/ab_test.dart
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// @dart = 2.8
-
 import 'package:flutter_devicelab/framework/ab.dart';
 import 'package:flutter_devicelab/framework/task_result.dart';
 
diff --git a/dev/devicelab/test/adb_test.dart b/dev/devicelab/test/adb_test.dart
index bd9cb6a..d49bfad 100644
--- a/dev/devicelab/test/adb_test.dart
+++ b/dev/devicelab/test/adb_test.dart
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// @dart = 2.8
-
 import 'package:collection/collection.dart' show ListEquality, MapEquality;
 
 import 'package:flutter_devicelab/framework/devices.dart';
@@ -13,12 +11,11 @@
 
 void main() {
   group('device', () {
-    Device device;
+    late Device device;
 
     setUp(() {
       FakeDevice.resetLog();
-      device = null;
-      device = FakeDevice();
+      device = FakeDevice(deviceId: 'fakeDeviceId');
     });
 
     tearDown(() {
@@ -131,9 +128,9 @@
 }
 
 CommandArgs cmd({
-  String command,
-  List<String> arguments,
-  Map<String, String> environment,
+  required String command,
+  List<String>? arguments,
+  Map<String, String>? environment,
 }) {
   return CommandArgs(
     command: command,
@@ -146,11 +143,11 @@
 
 @immutable
 class CommandArgs {
-  const CommandArgs({ this.command, this.arguments, this.environment });
+  const CommandArgs({ required this.command, this.arguments, this.environment });
 
   final String command;
-  final List<String> arguments;
-  final Map<String, String> environment;
+  final List<String>? arguments;
+  final Map<String, String>? environment;
 
   @override
   String toString() => 'CommandArgs(command: $command, arguments: $arguments, environment: $environment)';
@@ -177,7 +174,7 @@
 }
 
 class FakeDevice extends AndroidDevice {
-  FakeDevice({String deviceId}) : super(deviceId: deviceId);
+  FakeDevice({required String deviceId}) : super(deviceId: deviceId);
 
   static String output = '';
 
@@ -206,7 +203,7 @@
   }
 
   @override
-  Future<String> shellEval(String command, List<String> arguments, { Map<String, String> environment, bool silent = false }) async {
+  Future<String> shellEval(String command, List<String> arguments, { Map<String, String>? environment, bool silent = false }) async {
     commandLog.add(CommandArgs(
       command: command,
       arguments: arguments,
@@ -216,7 +213,7 @@
   }
 
   @override
-  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 {
     commandLog.add(CommandArgs(
       command: command,
       arguments: arguments,
diff --git a/dev/devicelab/test/cocoon_test.dart b/dev/devicelab/test/cocoon_test.dart
index 26c2e43..c0f6db6 100644
--- a/dev/devicelab/test/cocoon_test.dart
+++ b/dev/devicelab/test/cocoon_test.dart
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// @dart = 2.8
-
 import 'dart:convert';
 import 'dart:io';
 
@@ -17,14 +15,14 @@
 import 'common.dart';
 
 void main() {
-  ProcessResult _processResult;
+  late ProcessResult _processResult;
   ProcessResult runSyncStub(String executable, List<String> args,
-          {Map<String, String> environment,
-          bool includeParentEnvironment,
-          bool runInShell,
-          Encoding stderrEncoding,
-          Encoding stdoutEncoding,
-          String workingDirectory}) =>
+          {Map<String, String>? environment,
+          bool includeParentEnvironment = true,
+          bool runInShell = false,
+          Encoding? stderrEncoding,
+          Encoding? stdoutEncoding,
+          String? workingDirectory}) =>
       _processResult;
 
   // Expected test values.
@@ -33,9 +31,9 @@
   const String serviceAccountToken = 'test_token';
 
   group('Cocoon', () {
-    Client mockClient;
-    Cocoon cocoon;
-    FileSystem fs;
+    late Client mockClient;
+    late Cocoon cocoon;
+    late FileSystem fs;
 
     setUp(() {
       fs = MemoryFileSystem();
@@ -183,7 +181,7 @@
   });
 
   group('AuthenticatedCocoonClient', () {
-    FileSystem fs;
+    late FileSystem fs;
 
     setUp(() {
       fs = MemoryFileSystem();
diff --git a/dev/devicelab/test/host_agent_test.dart b/dev/devicelab/test/host_agent_test.dart
index 50bad78..a63fa4c 100644
--- a/dev/devicelab/test/host_agent_test.dart
+++ b/dev/devicelab/test/host_agent_test.dart
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// @dart = 2.8
-
 import 'package:file/file.dart';
 import 'package:file/memory.dart';
 import 'package:flutter_devicelab/framework/host_agent.dart';
@@ -12,7 +10,7 @@
 import 'common.dart';
 
 void main() {
-  FileSystem fs;
+  late FileSystem fs;
   setUp(() {
     fs = MemoryFileSystem();
     hostAgent.resetDumpDirectory();
@@ -31,8 +29,8 @@
       );
       final HostAgent agent = HostAgent(platform: fakePlatform, fileSystem: fs);
 
-      expect(agent.dumpDirectory.existsSync(), isTrue);
-      expect(agent.dumpDirectory.path, environmentDir.path);
+      expect(agent.dumpDirectory!.existsSync(), isTrue);
+      expect(agent.dumpDirectory!.path, environmentDir.path);
     });
 
     test('not set by environment', () async {
diff --git a/dev/devicelab/test/run_test.dart b/dev/devicelab/test/run_test.dart
index 4a11679..61ff3ea 100644
--- a/dev/devicelab/test/run_test.dart
+++ b/dev/devicelab/test/run_test.dart
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// @dart = 2.8
-
 import 'dart:io';
 
 import 'package:path/path.dart' as path;
@@ -31,7 +29,7 @@
     Future<void> expectScriptResult(
         List<String> testNames,
         int expectedExitCode,
-        {String deviceId}
+        {String? deviceId}
       ) async {
       final ProcessResult result = await runScript(testNames, <String>[
         if (deviceId != null) ...<String>['-d', deviceId],
diff --git a/dev/devicelab/test/running_processes_test.dart b/dev/devicelab/test/running_processes_test.dart
index 1d67632..ec27b8e 100644
--- a/dev/devicelab/test/running_processes_test.dart
+++ b/dev/devicelab/test/running_processes_test.dart
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// @dart = 2.8
-
 import 'package:flutter_devicelab/framework/running_processes.dart';
 import 'common.dart';
 
diff --git a/dev/devicelab/test/task_result_test.dart b/dev/devicelab/test/task_result_test.dart
index 7c8109d..0789d5b 100644
--- a/dev/devicelab/test/task_result_test.dart
+++ b/dev/devicelab/test/task_result_test.dart
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// @dart = 2.8
-
 import 'package:flutter_devicelab/framework/task_result.dart';
 
 import 'common.dart';
diff --git a/dev/devicelab/test/tasks/build_test_task_test.dart b/dev/devicelab/test/tasks/build_test_task_test.dart
index 343ad5d..5cc8b98 100644
--- a/dev/devicelab/test/tasks/build_test_task_test.dart
+++ b/dev/devicelab/test/tasks/build_test_task_test.dart
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// @dart = 2.8
-
 import 'dart:async';
 
 import 'package:flutter_devicelab/framework/runner.dart';
@@ -24,7 +22,7 @@
       deviceId: 'FAKE_SUCCESS',
       isolateParams: isolateParams,
     );
-    expect(result.data['benchmark'], 'data');
+    expect(result.data!['benchmark'], 'data');
   });
 
   test('runs build only when build arg is given', () async {
@@ -44,7 +42,7 @@
       deviceId: 'FAKE_SUCCESS',
       isolateParams: isolateParams,
     );
-    expect(result.data['benchmark'], 'data');
+    expect(result.data!['benchmark'], 'data');
   });
 
   test('sets environment', () async {
diff --git a/dev/devicelab/test/utils_test.dart b/dev/devicelab/test/utils_test.dart
index 1ea95b3..06db6a1 100644
--- a/dev/devicelab/test/utils_test.dart
+++ b/dev/devicelab/test/utils_test.dart
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// @dart = 2.8
-
 import 'package:flutter_devicelab/framework/utils.dart';
 
 import 'common.dart';