Migrate assemble and integration_test_device to null safety (#96630)

diff --git a/packages/flutter_tools/lib/src/commands/assemble.dart b/packages/flutter_tools/lib/src/commands/assemble.dart
index 369a3c9..928181d 100644
--- a/packages/flutter_tools/lib/src/commands/assemble.dart
+++ b/packages/flutter_tools/lib/src/commands/assemble.dart
@@ -2,10 +2,10 @@
 // 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:args/args.dart';
 import 'package:meta/meta.dart';
 
+import '../artifacts.dart';
 import '../base/common.dart';
 import '../base/file_system.dart';
 import '../build_info.dart';
@@ -88,7 +88,7 @@
 /// Assemble provides a low level API to interact with the flutter tool build
 /// system.
 class AssembleCommand extends FlutterCommand {
-  AssembleCommand({ bool verboseHelp = false, @required BuildSystem buildSystem })
+  AssembleCommand({ bool verboseHelp = false, required BuildSystem buildSystem })
     : _buildSystem = buildSystem {
     argParser.addMultiOption(
       'define',
@@ -161,13 +161,13 @@
 
   @override
   Future<Set<DevelopmentArtifact>> get requiredArtifacts async {
-    final String platform = environment.defines[kTargetPlatform];
+    final String? platform = environment.defines[kTargetPlatform];
     if (platform == null) {
       return super.requiredArtifacts;
     }
 
     final TargetPlatform targetPlatform = getTargetPlatformForName(platform);
-    final DevelopmentArtifact artifact = artifactFromTargetPlatform(targetPlatform);
+    final DevelopmentArtifact? artifact = artifactFromTargetPlatform(targetPlatform);
     if (artifact != null) {
       return <DevelopmentArtifact>{artifact};
     }
@@ -176,18 +176,19 @@
 
   /// The target(s) we are building.
   List<Target> createTargets() {
-    if (argResults.rest.isEmpty) {
+    final ArgResults argumentResults = argResults!;
+    if (argumentResults.rest.isEmpty) {
       throwToolExit('missing target name for flutter assemble.');
     }
-    final String name = argResults.rest.first;
+    final String name = argumentResults.rest.first;
     final Map<String, Target> targetMap = <String, Target>{
       for (final Target target in _kDefaultTargets)
         target.name: target
     };
     final List<Target> results = <Target>[
-      for (final String targetName in argResults.rest)
+      for (final String targetName in argumentResults.rest)
         if (targetMap.containsKey(targetName))
-          targetMap[targetName]
+          targetMap[targetName]!
     ];
     if (results.isEmpty) {
       throwToolExit('No target named "$name" defined.');
@@ -196,7 +197,7 @@
   }
 
   bool isDeferredComponentsTargets() {
-    for (final String targetName in argResults.rest) {
+    for (final String targetName in argResults!.rest) {
       if (deferredComponentsTargets.contains(targetName)) {
         return true;
       }
@@ -205,7 +206,7 @@
   }
 
   bool isDebug() {
-    for (final String targetName in argResults.rest) {
+    for (final String targetName in argResults!.rest) {
       if (targetName.contains('debug')) {
         return true;
       }
@@ -213,13 +214,12 @@
     return false;
   }
 
-  Environment get environment => _environment ??= createEnvironment();
-  Environment _environment;
+  late final Environment environment = createEnvironment();
 
   /// The environmental configuration for a build invocation.
   Environment createEnvironment() {
     final FlutterProject flutterProject = FlutterProject.current();
-    String output = stringArg('output');
+    String? output = stringArg('output');
     if (output == null) {
       throwToolExit('--output directory is required for assemble.');
     }
@@ -227,6 +227,7 @@
     if (globals.fs.path.isRelative(output)) {
       output = globals.fs.path.join(flutterProject.directory.path, output);
     }
+    final Artifacts artifacts = globals.artifacts!;
     final Environment result = Environment(
       outputDir: globals.fs.directory(output),
       buildDir: flutterProject.directory
@@ -237,12 +238,12 @@
       inputs: _parseDefines(stringsArg('input')),
       cacheDir: globals.cache.getRoot(),
       flutterRootDir: globals.fs.directory(Cache.flutterRoot),
-      artifacts: globals.artifacts,
+      artifacts: artifacts,
       fileSystem: globals.fs,
       logger: globals.logger,
       processManager: globals.processManager,
       platform: globals.platform,
-      engineVersion: globals.artifacts.isLocalEngine
+      engineVersion: artifacts.isLocalEngine
         ? null
         : globals.flutterVersion.engineRevision,
       generateDartPluginRegistry: true,
@@ -261,18 +262,19 @@
       final String value = chunk.substring(indexEquals + 1);
       results[key] = value;
     }
-    if (argResults.wasParsed(FlutterOptions.kExtraGenSnapshotOptions)) {
-      results[kExtraGenSnapshotOptions] = (argResults[FlutterOptions.kExtraGenSnapshotOptions] as List<String>).join(',');
+    final ArgResults argumentResults = argResults!;
+    if (argumentResults.wasParsed(FlutterOptions.kExtraGenSnapshotOptions)) {
+      results[kExtraGenSnapshotOptions] = (argumentResults[FlutterOptions.kExtraGenSnapshotOptions] as List<String>).join(',');
     }
-    if (argResults.wasParsed(FlutterOptions.kDartDefinesOption)) {
-      results[kDartDefines] = (argResults[FlutterOptions.kDartDefinesOption] as List<String>).join(',');
+    if (argumentResults.wasParsed(FlutterOptions.kDartDefinesOption)) {
+      results[kDartDefines] = (argumentResults[FlutterOptions.kDartDefinesOption] as List<String>).join(',');
     }
     results[kDeferredComponents] = 'false';
     if (FlutterProject.current().manifest.deferredComponents != null && isDeferredComponentsTargets() && !isDebug()) {
       results[kDeferredComponents] = 'true';
     }
-    if (argResults.wasParsed(FlutterOptions.kExtraFrontEndOptions)) {
-      results[kExtraFrontEndOptions] = (argResults[FlutterOptions.kExtraFrontEndOptions] as List<String>).join(',');
+    if (argumentResults.wasParsed(FlutterOptions.kExtraFrontEndOptions)) {
+      results[kExtraFrontEndOptions] = (argumentResults[FlutterOptions.kExtraFrontEndOptions] as List<String>).join(',');
     }
     return results;
   }
@@ -289,7 +291,7 @@
         nonDeferredTargets.add(target);
       }
     }
-    Target target;
+    Target? target;
     List<String> decodedDefines;
     try {
       decodedDefines = decodeDartDefines(environment.defines, kDartDefines);
@@ -314,12 +316,13 @@
     } else if (targets.isNotEmpty) {
       target = targets.single;
     }
+    final ArgResults argumentResults = argResults!;
     final BuildResult result = await _buildSystem.build(
-      target,
+      target!,
       environment,
       buildSystemConfig: BuildSystemConfig(
-        resourcePoolSize: argResults.wasParsed('resource-pool-size')
-          ? int.tryParse(stringArg('resource-pool-size'))
+        resourcePoolSize: argumentResults.wasParsed('resource-pool-size')
+          ? int.tryParse(stringArg('resource-pool-size')!)
           : null,
         ),
       );
@@ -335,17 +338,17 @@
     }
     globals.printTrace('build succeeded.');
 
-    if (argResults.wasParsed('build-inputs')) {
-      writeListIfChanged(result.inputFiles, stringArg('build-inputs'));
+    if (argumentResults.wasParsed('build-inputs')) {
+      writeListIfChanged(result.inputFiles, stringArg('build-inputs')!);
     }
-    if (argResults.wasParsed('build-outputs')) {
-      writeListIfChanged(result.outputFiles, stringArg('build-outputs'));
+    if (argumentResults.wasParsed('build-outputs')) {
+      writeListIfChanged(result.outputFiles, stringArg('build-outputs')!);
     }
-    if (argResults.wasParsed('performance-measurement-file')) {
-      final File outFile = globals.fs.file(argResults['performance-measurement-file']);
+    if (argumentResults.wasParsed('performance-measurement-file')) {
+      final File outFile = globals.fs.file(argumentResults['performance-measurement-file']);
       writePerformanceData(result.performance.values, outFile);
     }
-    if (argResults.wasParsed('depfile')) {
+    if (argumentResults.wasParsed('depfile')) {
       final File depfileFile = globals.fs.file(stringArg('depfile'));
       final Depfile depfile = Depfile(result.inputFiles, result.outputFiles);
       final DepfileService depfileService = DepfileService(
diff --git a/packages/flutter_tools/lib/src/test/integration_test_device.dart b/packages/flutter_tools/lib/src/test/integration_test_device.dart
index 74feba4..2782752 100644
--- a/packages/flutter_tools/lib/src/test/integration_test_device.dart
+++ b/packages/flutter_tools/lib/src/test/integration_test_device.dart
@@ -2,11 +2,8 @@
 // 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:meta/meta.dart';
 import 'package:stream_channel/stream_channel.dart';
 import 'package:vm_service/vm_service.dart' as vm_service;
 
@@ -24,10 +21,10 @@
 
 class IntegrationTestTestDevice implements TestDevice {
   IntegrationTestTestDevice({
-    @required this.id,
-    @required this.device,
-    @required this.debuggingOptions,
-    @required this.userIdentifier,
+    required this.id,
+    required this.device,
+    required this.debuggingOptions,
+    required this.userIdentifier,
   });
 
   final int id;
@@ -35,7 +32,7 @@
   final DebuggingOptions debuggingOptions;
   final String userIdentifier;
 
-  ApplicationPackage _applicationPackage;
+  ApplicationPackage? _applicationPackage;
   final Completer<void> _finished = Completer<void>();
   final Completer<Uri> _gotProcessObservatoryUri = Completer<Uri>();
 
@@ -45,13 +42,16 @@
   @override
   Future<StreamChannel<String>> start(String entrypointPath) async {
     final TargetPlatform targetPlatform = await device.targetPlatform;
-    _applicationPackage = await ApplicationPackageFactory.instance.getPackageForPlatform(
+    _applicationPackage = await ApplicationPackageFactory.instance?.getPackageForPlatform(
       targetPlatform,
       buildInfo: debuggingOptions.buildInfo,
     );
+    if (_applicationPackage == null) {
+      throw TestDeviceException('No application found for $targetPlatform.', StackTrace.current);
+    }
 
     final LaunchResult launchResult = await device.startApp(
-      _applicationPackage,
+      _applicationPackage!,
       mainPath: entrypointPath,
       platformArgs: <String, dynamic>{},
       debuggingOptions: debuggingOptions,
@@ -60,17 +60,18 @@
     if (!launchResult.started) {
       throw TestDeviceException('Unable to start the app on the device.', StackTrace.current);
     }
-    if (launchResult.observatoryUri == null) {
+    final Uri? observatoryUri = launchResult.observatoryUri;
+    if (observatoryUri == null) {
       throw TestDeviceException('Observatory is not available on the test device.', StackTrace.current);
     }
 
     // No need to set up the log reader because the logs are captured and
     // streamed to the package:test_core runner.
 
-    _gotProcessObservatoryUri.complete(launchResult.observatoryUri);
+    _gotProcessObservatoryUri.complete(observatoryUri);
 
     globals.printTrace('test $id: Connecting to vm service');
-    final FlutterVmService vmService = await connectToVmService(launchResult.observatoryUri, logger: globals.logger).timeout(
+    final FlutterVmService vmService = await connectToVmService(observatoryUri, logger: globals.logger).timeout(
       const Duration(seconds: 5),
       onTimeout: () => throw TimeoutException('Connecting to the VM Service timed out.'),
     );
@@ -83,7 +84,7 @@
     await vmService.service.streamListen(vm_service.EventStreams.kExtension);
     final Stream<String> remoteMessages = vmService.service.onExtensionEvent
         .where((vm_service.Event e) => e.extensionKind == kIntegrationTestExtension)
-        .map((vm_service.Event e) => e.extensionData.data[kIntegrationTestData] as String);
+        .map((vm_service.Event e) => e.extensionData!.data[kIntegrationTestData] as String);
 
     final StreamChannelController<String> controller = StreamChannelController<String>();
 
@@ -113,11 +114,14 @@
 
   @override
   Future<void> kill() async {
-    if (!await device.stopApp(_applicationPackage, userIdentifier: userIdentifier)) {
-      globals.printTrace('Could not stop the Integration Test app.');
-    }
-    if (!await device.uninstallApp(_applicationPackage, userIdentifier: userIdentifier)) {
-      globals.printTrace('Could not uninstall the Integration Test app.');
+    final ApplicationPackage? applicationPackage = _applicationPackage;
+    if (applicationPackage != null) {
+      if (!await device.stopApp(applicationPackage, userIdentifier: userIdentifier)) {
+        globals.printTrace('Could not stop the Integration Test app.');
+      }
+      if (!await device.uninstallApp(applicationPackage, userIdentifier: userIdentifier)) {
+        globals.printTrace('Could not uninstall the Integration Test app.');
+      }
     }
 
     await device.dispose();
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
index 0c0791a..0511ac1 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
@@ -750,21 +750,6 @@
   }
 }
 
-class FakeApplicationPackageFactory extends Fake implements ApplicationPackageFactory {
-  FakeApplicationPackageFactory(this.applicationPackage);
-
-  ApplicationPackage applicationPackage;
-
-  @override
-  Future<ApplicationPackage> getPackageForPlatform(
-      TargetPlatform platform, {
-        BuildInfo buildInfo,
-        File applicationBinary,
-      }) async => applicationPackage;
-}
-
-class FakeApplicationPackage extends Fake implements ApplicationPackage { }
-
 class TestRunCommandWithFakeResidentRunner extends RunCommand {
   FakeResidentRunner fakeResidentRunner;
 
diff --git a/packages/flutter_tools/test/general.shard/integration_test_device_test.dart b/packages/flutter_tools/test/general.shard/integration_test_device_test.dart
index 90c450f..c762323 100644
--- a/packages/flutter_tools/test/general.shard/integration_test_device_test.dart
+++ b/packages/flutter_tools/test/general.shard/integration_test_device_test.dart
@@ -4,6 +4,8 @@
 
 // @dart = 2.8
 
+import 'package:file/file.dart';
+import 'package:flutter_tools/src/application_package.dart';
 import 'package:flutter_tools/src/base/io.dart' as io;
 import 'package:flutter_tools/src/base/logger.dart';
 import 'package:flutter_tools/src/build_info.dart';
@@ -12,8 +14,10 @@
 import 'package:flutter_tools/src/test/test_device.dart';
 import 'package:flutter_tools/src/vmservice.dart';
 import 'package:stream_channel/stream_channel.dart';
+import 'package:test/fake.dart';
 import 'package:vm_service/vm_service.dart' as vm_service;
 
+import '../src/common.dart';
 import '../src/context.dart';
 import '../src/fake_devices.dart';
 import '../src/fake_vm_services.dart';
@@ -121,12 +125,26 @@
     ]);
   });
 
+  testUsingContext('will not start when package missing', () async {
+    await expectLater(
+      testDevice.start('entrypointPath'),
+      throwsA(
+        isA<TestDeviceException>().having(
+          (Exception e) => e.toString(),
+          'description',
+          contains('No application found for TargetPlatform.android_arm'),
+        ),
+      ),
+    );
+  });
+
   testUsingContext('Can start the entrypoint', () async {
     await testDevice.start('entrypointPath');
 
     expect(await testDevice.observatoryUri, observatoryUri);
     expect(testDevice.finished, doesNotComplete);
   }, overrides: <Type, Generator>{
+    ApplicationPackageFactory: () => FakeApplicationPackageFactory(),
     VMServiceConnector: () => (Uri httpUri, {
       ReloadSources reloadSources,
       Restart restart,
@@ -145,6 +163,7 @@
 
     expect(testDevice.finished, completes);
   }, overrides: <Type, Generator>{
+    ApplicationPackageFactory: () => FakeApplicationPackageFactory(),
     VMServiceConnector: () => (Uri httpUri, {
       ReloadSources reloadSources,
       Restart restart,
@@ -218,6 +237,7 @@
     await fakeVmServiceHost.vmService.dispose();
     expect(await channel.stream.isEmpty, true);
   }, overrides: <Type, Generator>{
+    ApplicationPackageFactory: () => FakeApplicationPackageFactory(),
     VMServiceConnector: () => (Uri httpUri, {
       ReloadSources reloadSources,
       Restart restart,
@@ -230,3 +250,14 @@
     }) async => fakeVmServiceHost.vmService,
   });
 }
+
+class FakeApplicationPackageFactory extends Fake implements ApplicationPackageFactory {
+  @override
+  Future<ApplicationPackage> getPackageForPlatform(
+    TargetPlatform platform, {
+    BuildInfo buildInfo,
+    File applicationBinary,
+  }) async => FakeApplicationPackage();
+}
+
+class FakeApplicationPackage extends Fake implements ApplicationPackage { }