[flutter_tools] Remove mocking and simplify Dart target tests (#50688)

diff --git a/packages/flutter_tools/lib/src/compile.dart b/packages/flutter_tools/lib/src/compile.dart
index e89baa5..4df5b1d 100644
--- a/packages/flutter_tools/lib/src/compile.dart
+++ b/packages/flutter_tools/lib/src/compile.dart
@@ -241,7 +241,8 @@
   }
 }
 
-List<String> _buildModeOptions(BuildMode mode) {
+/// List the preconfigured build options for a given build mode.
+List<String> buildModeOptions(BuildMode mode) {
   switch (mode) {
     case BuildMode.debug:
       return <String>[
@@ -316,7 +317,7 @@
       '-Ddart.developer.causal_async_stacks=${buildMode == BuildMode.debug}',
       for (final Object dartDefine in dartDefines)
         '-D$dartDefine',
-      ..._buildModeOptions(buildMode),
+      ...buildModeOptions(buildMode),
       if (trackWidgetCreation) '--track-widget-creation',
       if (!linkPlatformKernelIn) '--no-link-platform',
       if (aot) ...<String>[
@@ -357,14 +358,9 @@
     ];
 
     globals.printTrace(command.join(' '));
-    final Process server = await globals.processManager
-      .start(command)
-      .catchError((dynamic error, StackTrace stack) {
-        globals.printError('Failed to start frontend server $error, $stack');
-      });
+    final Process server = await globals.processManager.start(command);
 
     final StdoutHandler _stdoutHandler = StdoutHandler();
-
     server.stderr
       .transform<String>(utf8.decoder)
       .listen(globals.printError);
@@ -678,7 +674,7 @@
         '--packages',
         packagesPath,
       ],
-      ..._buildModeOptions(buildMode),
+      ...buildModeOptions(buildMode),
       if (trackWidgetCreation) '--track-widget-creation',
       if (fileSystemRoots != null)
         for (final String root in fileSystemRoots) ...<String>[
diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/dart_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/dart_test.dart
index 1afc129..cac3fbb 100644
--- a/packages/flutter_tools/test/general.shard/build_system/targets/dart_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_system/targets/dart_test.dart
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'package:flutter_tools/src/base/build.dart';
+import 'package:file/memory.dart';
+import 'package:flutter_tools/src/artifacts.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
-import 'package:flutter_tools/src/base/process.dart';
 import 'package:flutter_tools/src/build_info.dart';
 import 'package:flutter_tools/src/build_system/build_system.dart';
 import 'package:flutter_tools/src/build_system/exceptions.dart';
@@ -12,32 +12,30 @@
 import 'package:flutter_tools/src/build_system/targets/ios.dart';
 import 'package:flutter_tools/src/cache.dart';
 import 'package:flutter_tools/src/compile.dart';
-import 'package:flutter_tools/src/macos/xcode.dart';
-import 'package:flutter_tools/src/project.dart';
 import 'package:flutter_tools/src/globals.dart' as globals;
-
-import 'package:mockito/mockito.dart';
+import 'package:platform/platform.dart';
 import 'package:process/process.dart';
 
 import '../../../src/common.dart';
-import '../../../src/mocks.dart';
+import '../../../src/fake_process_manager.dart';
 import '../../../src/testbed.dart';
 
+const String kBoundaryKey = '4d2d9609-c662-4571-afde-31410f96caa6';
+const String kElfAot = '--snapshot_kind=app-aot-elf';
+const String kAssemblyAot = '--snapshot_kind=app-aot-assembly';
+
 void main() {
-  const BuildSystem buildSystem = BuildSystem();
   Testbed testbed;
+  FakeProcessManager processManager;
   Environment androidEnvironment;
   Environment iosEnvironment;
-  MockProcessManager mockProcessManager;
-  MockXcode mockXcode;
+  Artifacts artifacts;
 
   setUpAll(() {
     Cache.disableLocking();
   });
 
   setUp(() {
-    mockXcode = MockXcode();
-    mockProcessManager = MockProcessManager();
     testbed = Testbed(setup: () {
       androidEnvironment = Environment.test(
         globals.fs.currentDirectory,
@@ -46,6 +44,7 @@
           kTargetPlatform: getNameForTargetPlatform(TargetPlatform.android_arm),
         },
       );
+      androidEnvironment.buildDir.createSync(recursive: true);
       iosEnvironment = Environment.test(
         globals.fs.currentDirectory,
         defines: <String, String>{
@@ -53,427 +52,462 @@
           kTargetPlatform: getNameForTargetPlatform(TargetPlatform.ios),
         },
       );
-      HostPlatform hostPlatform;
-      if (globals.platform.isWindows) {
-        hostPlatform = HostPlatform.windows_x64;
-      } else if (globals.platform.isLinux) {
-        hostPlatform = HostPlatform.linux_x64;
-      } else if (globals.platform.isMacOS) {
-        hostPlatform = HostPlatform.darwin_x64;
-      } else {
-        assert(false);
-      }
-
-      final String engineArtifacts = globals.fs.path.join('bin', 'cache',
-          'artifacts', 'engine');
-      final List<String> paths = <String>[
-        globals.fs.path.join('bin', 'cache', 'dart-sdk', 'bin', 'dart'),
-        globals.fs.path.join('bin', 'cache', 'dart-sdk', 'bin', 'dart.exe'),
-        globals.fs.path.join(engineArtifacts, getNameForHostPlatform(hostPlatform),
-            'frontend_server.dart.snapshot'),
-        globals.fs.path.join(engineArtifacts, 'android-arm-profile',
-            getNameForHostPlatform(hostPlatform), 'gen_snapshot'),
-        globals.fs.path.join(engineArtifacts, 'ios-profile', 'gen_snapshot'),
-        globals.fs.path.join(engineArtifacts, 'common', 'flutter_patched_sdk',
-            'platform_strong.dill'),
-        globals.fs.path.join('lib', 'foo.dart'),
-        globals.fs.path.join('lib', 'bar.dart'),
-        globals.fs.path.join('lib', 'fizz'),
-        globals.fs.path.join('packages', 'flutter_tools', 'lib', 'src', 'build_system', 'targets', 'dart.dart'),
-        globals.fs.path.join('packages', 'flutter_tools', 'lib', 'src', 'build_system', 'targets', 'ios.dart'),
-      ];
-      for (final String path in paths) {
-        globals.fs.file(path).createSync(recursive: true);
-      }
+      iosEnvironment.buildDir.createSync(recursive: true);
+      artifacts = CachedArtifacts(
+        cache: globals.cache,
+        platform: globals.platform,
+        fileSystem: globals.fs,
+      );
     }, overrides: <Type, Generator>{
-      KernelCompilerFactory: () => FakeKernelCompilerFactory(),
-      GenSnapshot: () => FakeGenSnapshot(),
+      Platform: () => FakePlatform(operatingSystem: 'macos', environment: <String, String>{}),
+      FileSystem: () => MemoryFileSystem.test(style: FileSystemStyle.posix),
+      ProcessManager: () => processManager,
     });
   });
 
-  test('kernel_snapshot Produces correct output directory', () => testbed.run(() async {
-    await buildSystem.build(const KernelSnapshot(), androidEnvironment);
-
-    expect(globals.fs.file(globals.fs.path.join(androidEnvironment.buildDir.path,'app.dill')).existsSync(), true);
+  test('KernelSnapshot throws error if missing build mode', () => testbed.run(() async {
+    androidEnvironment.defines.remove(kBuildMode);
+    expect(
+      const KernelSnapshot().build(androidEnvironment),
+      throwsA(isInstanceOf<MissingDefineException>()));
   }));
 
-  test('kernel_snapshot throws error if missing build mode', () => testbed.run(() async {
-    final BuildResult result = await buildSystem.build(const KernelSnapshot(),
-        androidEnvironment..defines.remove(kBuildMode));
+  test('KernelSnapshot handles null result from kernel compilation', () => testbed.run(() async {
+    globals.fs.file('.packages').writeAsStringSync('\n');
+    final String build = androidEnvironment.buildDir.path;
+    processManager = FakeProcessManager.list(<FakeCommand>[
+      FakeCommand(command: <String>[
+        artifacts.getArtifactPath(Artifact.engineDartBinary),
+        artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk),
+        '--sdk-root',
+        artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath) + '/',
+        '--target=flutter',
+        '-Ddart.developer.causal_async_stacks=false',
+        ...buildModeOptions(BuildMode.profile),
+        '--aot',
+        '--tfa',
+        '--packages',
+        '/.packages',
+        '--output-dill',
+        '$build/app.dill',
+        '--depfile',
+        '$build/kernel_snapshot.d',
+        '/lib/main.dart',
+      ], exitCode: 1),
+    ]);
 
-    expect(result.exceptions.values.single.exception, isA<MissingDefineException>());
+    await expectLater(() => const KernelSnapshot().build(androidEnvironment),
+      throwsA(isA<Exception>()));
+    expect(processManager.hasRemainingExpectations, false);
   }));
 
-  test('kernel_snapshot handles null result from kernel compilation', () => testbed.run(() async {
-    final FakeKernelCompilerFactory fakeKernelCompilerFactory = kernelCompilerFactory as FakeKernelCompilerFactory;
-    fakeKernelCompilerFactory.kernelCompiler = MockKernelCompiler();
-    when(fakeKernelCompilerFactory.kernelCompiler.compile(
-      sdkRoot: anyNamed('sdkRoot'),
-      mainPath: anyNamed('mainPath'),
-      outputFilePath: anyNamed('outputFilePath'),
-      depFilePath: anyNamed('depFilePath'),
-      targetModel: anyNamed('targetModel'),
-      linkPlatformKernelIn: anyNamed('linkPlatformKernelIn'),
-      aot: anyNamed('aot'),
-      buildMode: anyNamed('buildMode'),
-      trackWidgetCreation: anyNamed('trackWidgetCreation'),
-      extraFrontEndOptions: anyNamed('extraFrontEndOptions'),
-      packagesPath: anyNamed('packagesPath'),
-      fileSystemRoots: anyNamed('fileSystemRoots'),
-      fileSystemScheme: anyNamed('fileSystemScheme'),
-      platformDill: anyNamed('platformDill'),
-      initializeFromDill: anyNamed('initializeFromDill'),
-      dartDefines: anyNamed('dartDefines'),
-    )).thenAnswer((Invocation invocation) async {
-      return null;
-    });
-    final BuildResult result = await buildSystem.build(const KernelSnapshot(), androidEnvironment);
-
-    expect(result.exceptions.values.single.exception, isA<Exception>());
-  }));
-
-  test('kernel_snapshot does not use track widget creation on profile builds', () => testbed.run(() async {
-    final MockKernelCompiler mockKernelCompiler = MockKernelCompiler();
-    when(kernelCompilerFactory.create(any)).thenAnswer((Invocation _) async {
-      return mockKernelCompiler;
-    });
-    when(mockKernelCompiler.compile(
-      sdkRoot: anyNamed('sdkRoot'),
-      aot: anyNamed('aot'),
-      buildMode: anyNamed('buildMode'),
-      trackWidgetCreation: false,
-      targetModel: anyNamed('targetModel'),
-      outputFilePath: anyNamed('outputFilePath'),
-      depFilePath: anyNamed('depFilePath'),
-      packagesPath: anyNamed('packagesPath'),
-      mainPath: anyNamed('mainPath'),
-      extraFrontEndOptions: anyNamed('extraFrontEndOptions'),
-      fileSystemRoots: anyNamed('fileSystemRoots'),
-      fileSystemScheme: anyNamed('fileSystemScheme'),
-      linkPlatformKernelIn: anyNamed('linkPlatformKernelIn'),
-      dartDefines: anyNamed('dartDefines'),
-    )).thenAnswer((Invocation _) async {
-      return const CompilerOutput('example', 0, <Uri>[]);
-    });
+  test('KernelSnapshot does not use track widget creation on profile builds', () => testbed.run(() async {
+    globals.fs.file('.packages').writeAsStringSync('\n');
+    final String build = androidEnvironment.buildDir.path;
+    processManager = FakeProcessManager.list(<FakeCommand>[
+      FakeCommand(command: <String>[
+        artifacts.getArtifactPath(Artifact.engineDartBinary),
+        artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk),
+        '--sdk-root',
+        artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath) + '/',
+        '--target=flutter',
+        '-Ddart.developer.causal_async_stacks=false',
+        ...buildModeOptions(BuildMode.profile),
+        '--aot',
+        '--tfa',
+        '--packages',
+        '/.packages',
+        '--output-dill',
+        '$build/app.dill',
+        '--depfile',
+        '$build/kernel_snapshot.d',
+        '/lib/main.dart',
+      ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'),
+    ]);
 
     await const KernelSnapshot().build(androidEnvironment);
-  }, overrides: <Type, Generator>{
-    KernelCompilerFactory: () => MockKernelCompilerFactory(),
+
+    expect(processManager.hasRemainingExpectations, false);
   }));
 
-  test('kernel_snapshot can disable track-widget-creation on debug builds', () => testbed.run(() async {
-    final MockKernelCompiler mockKernelCompiler = MockKernelCompiler();
-    when(kernelCompilerFactory.create(any)).thenAnswer((Invocation _) async {
-      return mockKernelCompiler;
-    });
-    when(mockKernelCompiler.compile(
-      sdkRoot: anyNamed('sdkRoot'),
-      aot: anyNamed('aot'),
-      buildMode: anyNamed('buildMode'),
-      trackWidgetCreation: false,
-      targetModel: anyNamed('targetModel'),
-      outputFilePath: anyNamed('outputFilePath'),
-      depFilePath: anyNamed('depFilePath'),
-      packagesPath: anyNamed('packagesPath'),
-      mainPath: anyNamed('mainPath'),
-      extraFrontEndOptions: anyNamed('extraFrontEndOptions'),
-      fileSystemRoots: anyNamed('fileSystemRoots'),
-      fileSystemScheme: anyNamed('fileSystemScheme'),
-      linkPlatformKernelIn: false,
-      dartDefines: anyNamed('dartDefines'),
-    )).thenAnswer((Invocation _) async {
-      return const CompilerOutput('example', 0, <Uri>[]);
-    });
+  test('KernelSnapshot can disable track-widget-creation on debug builds', () => testbed.run(() async {
+    globals.fs.file('.packages').writeAsStringSync('\n');
+    final String build = androidEnvironment.buildDir.path;
+    processManager = FakeProcessManager.list(<FakeCommand>[
+      FakeCommand(command: <String>[
+        artifacts.getArtifactPath(Artifact.engineDartBinary),
+        artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk),
+        '--sdk-root',
+        artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath) + '/',
+        '--target=flutter',
+        '-Ddart.developer.causal_async_stacks=true',
+        ...buildModeOptions(BuildMode.debug),
+        '--no-link-platform',
+        '--packages',
+        '/.packages',
+        '--output-dill',
+        '$build/app.dill',
+        '--depfile',
+        '$build/kernel_snapshot.d',
+        '/lib/main.dart',
+      ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'),
+    ]);
 
     await const KernelSnapshot().build(androidEnvironment
-      ..defines[kBuildMode] = 'debug'
+      ..defines[kBuildMode] = getNameForBuildMode(BuildMode.debug)
       ..defines[kTrackWidgetCreation] = 'false');
-  }, overrides: <Type, Generator>{
-    KernelCompilerFactory: () => MockKernelCompilerFactory(),
+
+    expect(processManager.hasRemainingExpectations, false);
   }));
 
-  test('kernel_snapshot forces platform linking on debug for darwin target platforms', () => testbed.run(() async {
-    final MockKernelCompiler mockKernelCompiler = MockKernelCompiler();
-    when(kernelCompilerFactory.create(any)).thenAnswer((Invocation _) async {
-      return mockKernelCompiler;
-    });
-    when(mockKernelCompiler.compile(
-      sdkRoot: anyNamed('sdkRoot'),
-      aot: anyNamed('aot'),
-      buildMode: anyNamed('buildMode'),
-      trackWidgetCreation: anyNamed('trackWidgetCreation'),
-      targetModel: anyNamed('targetModel'),
-      outputFilePath: anyNamed('outputFilePath'),
-      depFilePath: anyNamed('depFilePath'),
-      packagesPath: anyNamed('packagesPath'),
-      mainPath: anyNamed('mainPath'),
-      extraFrontEndOptions: anyNamed('extraFrontEndOptions'),
-      fileSystemRoots: anyNamed('fileSystemRoots'),
-      fileSystemScheme: anyNamed('fileSystemScheme'),
-      linkPlatformKernelIn: true,
-      dartDefines: anyNamed('dartDefines'),
-    )).thenAnswer((Invocation _) async {
-      return const CompilerOutput('example', 0, <Uri>[]);
-    });
+  test('KernelSnapshot forces platform linking on debug for darwin target platforms', () => testbed.run(() async {
+    globals.fs.file('.packages').writeAsStringSync('\n');
+    final String build = androidEnvironment.buildDir.path;
+    processManager = FakeProcessManager.list(<FakeCommand>[
+      FakeCommand(command: <String>[
+        artifacts.getArtifactPath(Artifact.engineDartBinary),
+        artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk),
+        '--sdk-root',
+        artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath) + '/',
+        '--target=flutter',
+        '-Ddart.developer.causal_async_stacks=true',
+        ...buildModeOptions(BuildMode.debug),
+        '--packages',
+        '/.packages',
+        '--output-dill',
+        '$build/app.dill',
+        '--depfile',
+        '$build/kernel_snapshot.d',
+        '/lib/main.dart',
+      ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'),
+    ]);
 
     await const KernelSnapshot().build(androidEnvironment
-      ..defines[kTargetPlatform]  = 'darwin-x64'
-      ..defines[kBuildMode] = 'debug'
+      ..defines[kTargetPlatform]  = getNameForTargetPlatform(TargetPlatform.darwin_x64)
+      ..defines[kBuildMode] = getNameForBuildMode(BuildMode.debug)
       ..defines[kTrackWidgetCreation] = 'false'
     );
-  }, overrides: <Type, Generator>{
-    KernelCompilerFactory: () => MockKernelCompilerFactory(),
+
+    expect(processManager.hasRemainingExpectations, false);
   }));
 
-
-  test('kernel_snapshot does use track widget creation on debug builds', () => testbed.run(() async {
-    final MockKernelCompiler mockKernelCompiler = MockKernelCompiler();
-    when(kernelCompilerFactory.create(any)).thenAnswer((Invocation _) async {
-      return mockKernelCompiler;
-    });
-    when(mockKernelCompiler.compile(
-      sdkRoot: anyNamed('sdkRoot'),
-      aot: anyNamed('aot'),
-      buildMode: anyNamed('buildMode'),
-      trackWidgetCreation: true,
-      targetModel: anyNamed('targetModel'),
-      outputFilePath: anyNamed('outputFilePath'),
-      depFilePath: anyNamed('depFilePath'),
-      packagesPath: anyNamed('packagesPath'),
-      mainPath: anyNamed('mainPath'),
-      extraFrontEndOptions: anyNamed('extraFrontEndOptions'),
-      fileSystemRoots: anyNamed('fileSystemRoots'),
-      fileSystemScheme: anyNamed('fileSystemScheme'),
-      linkPlatformKernelIn: false,
-      dartDefines: anyNamed('dartDefines'),
-    )).thenAnswer((Invocation _) async {
-      return const CompilerOutput('example', 0, <Uri>[]);
-    });
-
-    await const KernelSnapshot().build(Environment.test(
+  test('KernelSnapshot does use track widget creation on debug builds', () => testbed.run(() async {
+    globals.fs.file('.packages').writeAsStringSync('\n');
+    final Environment testEnvironment = Environment.test(
       globals.fs.currentDirectory,
       defines: <String, String>{
-        kBuildMode: 'debug',
+        kBuildMode: getNameForBuildMode(BuildMode.debug),
         kTargetPlatform: getNameForTargetPlatform(TargetPlatform.android_arm),
-      }));
-  }, overrides: <Type, Generator>{
-    KernelCompilerFactory: () => MockKernelCompilerFactory(),
+      },
+    );
+    final String build = testEnvironment.buildDir.path;
+    processManager = FakeProcessManager.list(<FakeCommand>[
+      FakeCommand(command: <String>[
+        artifacts.getArtifactPath(Artifact.engineDartBinary),
+        artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk),
+        '--sdk-root',
+        artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath) + '/',
+        '--target=flutter',
+        '-Ddart.developer.causal_async_stacks=true',
+        ...buildModeOptions(BuildMode.debug),
+        '--track-widget-creation',
+        '--no-link-platform',
+        '--packages',
+        '/.packages',
+        '--output-dill',
+        '$build/app.dill',
+        '--depfile',
+        '$build/kernel_snapshot.d',
+        '/lib/main.dart',
+      ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey /build/653e11a8e6908714056a57cd6b4f602a/app.dill 0\n'),
+    ]);
+
+    await const KernelSnapshot().build(testEnvironment);
+
+    expect(processManager.hasRemainingExpectations, false);
   }));
 
-  test('aot_elf_profile Produces correct output directory', () => testbed.run(() async {
-    await buildSystem.build(const AotElfProfile(), androidEnvironment);
+  test('AotElfProfile Produces correct output directory', () => testbed.run(() async {
+    final String build = androidEnvironment.buildDir.path;
+    processManager = FakeProcessManager.list(<FakeCommand>[
+      FakeCommand(command: <String>[
+        artifacts.getArtifactPath(Artifact.genSnapshot, mode: BuildMode.profile),
+        '--deterministic',
+        kElfAot,
+        '--elf=$build/app.so',
+        '--strip',
+        '--no-sim-use-hardfp',
+        '--no-use-integer-division',
+        '--no-causal-async-stacks',
+        '--lazy-async-stacks',
+        '$build/app.dill',
+      ])
+    ]);
+    androidEnvironment.buildDir.childFile('app.dill').createSync(recursive: true);
 
-    expect(globals.fs.file(globals.fs.path.join(androidEnvironment.buildDir.path, 'app.dill')).existsSync(), true);
-    expect(globals.fs.file(globals.fs.path.join(androidEnvironment.buildDir.path, 'app.so')).existsSync(), true);
+    await const AotElfProfile().build(androidEnvironment);
+
+    expect(processManager.hasRemainingExpectations, false);
   }));
 
-  test('aot_elf_profile throws error if missing build mode', () => testbed.run(() async {
-    final BuildResult result = await buildSystem.build(const AotElfProfile(),
-        androidEnvironment..defines.remove(kBuildMode));
+  test('AotElfProfile throws error if missing build mode', () => testbed.run(() async {
+    androidEnvironment.defines.remove(kBuildMode);
 
-    expect(result.exceptions.values.single.exception, isA<MissingDefineException>());
+    expect(const AotElfProfile().build(androidEnvironment),
+      throwsA(isInstanceOf<MissingDefineException>()));
   }));
 
-  test('aot_elf_profile throws error if missing target platform', () => testbed.run(() async {
-    final BuildResult result = await buildSystem.build(const AotElfProfile(),
-        androidEnvironment..defines.remove(kTargetPlatform));
+  test('AotElfProfile throws error if missing target platform', () => testbed.run(() async {
+    androidEnvironment.defines.remove(kTargetPlatform);
 
-    expect(result.exceptions.values.single.exception, isA<MissingDefineException>());
+    expect(const AotElfProfile().build(androidEnvironment),
+      throwsA(isInstanceOf<MissingDefineException>()));
   }));
 
-  test('aot_assembly_profile throws error if missing build mode', () => testbed.run(() async {
-    final BuildResult result = await buildSystem.build(const AotAssemblyProfile(),
-        iosEnvironment..defines.remove(kBuildMode));
+  test('AotAssemblyProfile throws error if missing build mode', () => testbed.run(() async {
+    iosEnvironment.defines.remove(kBuildMode);
 
-    expect(result.exceptions.values.single.exception, isA<MissingDefineException>());
+    expect(const AotAssemblyProfile().build(iosEnvironment),
+      throwsA(isInstanceOf<MissingDefineException>()));
   }));
 
-  test('aot_assembly_profile throws error if missing target platform', () => testbed.run(() async {
-    final BuildResult result = await buildSystem.build(const AotAssemblyProfile(),
-        iosEnvironment..defines.remove(kTargetPlatform));
+  test('AotAssemblyProfile throws error if missing target platform', () => testbed.run(() async {
+    iosEnvironment.defines.remove(kTargetPlatform);
 
-    expect(result.exceptions.values.single.exception, isA<MissingDefineException>());
+    expect(const AotAssemblyProfile().build(iosEnvironment),
+      throwsA(isInstanceOf<MissingDefineException>()));
   }));
 
-  test('aot_assembly_profile throws error if built for non-iOS platform', () => testbed.run(() async {
-    final BuildResult result = await buildSystem
-        .build(const AotAssemblyProfile(), androidEnvironment);
-
-    expect(result.exceptions.values.single.exception, isA<Exception>());
+  test('AotAssemblyProfile throws error if built for non-iOS platform', () => testbed.run(() async {
+    expect(const AotAssemblyProfile().build(androidEnvironment),
+      throwsA(isInstanceOf<Exception>()));
   }));
 
-  test('aot_assembly_profile will lipo binaries together when multiple archs are requested', () => testbed.run(() async {
+  test('AotAssemblyProfile generates multiple arches and lipos together', () => testbed.run(() async {
+    final String build = iosEnvironment.buildDir.path;
+    processManager = FakeProcessManager.list(<FakeCommand>[
+      FakeCommand(command: <String>[
+        // This path is not known by the cache due to the iOS gen_snapshot split.
+        'bin/cache/artifacts/engine/ios-profile/gen_snapshot_armv7',
+        '--deterministic',
+        kAssemblyAot,
+        '--assembly=$build/armv7/snapshot_assembly.S',
+        '--strip',
+        '--no-sim-use-hardfp',
+        '--no-use-integer-division',
+        '--no-causal-async-stacks',
+        '--lazy-async-stacks',
+        '$build/app.dill',
+      ]),
+      FakeCommand(command: <String>[
+        // This path is not known by the cache due to the iOS gen_snapshot split.
+        'bin/cache/artifacts/engine/ios-profile/gen_snapshot_arm64',
+        '--deterministic',
+        kAssemblyAot,
+        '--assembly=$build/arm64/snapshot_assembly.S',
+        '--strip',
+        '--no-causal-async-stacks',
+        '--lazy-async-stacks',
+        '$build/app.dill',
+      ]),
+      const FakeCommand(command: <String>[
+        'xcrun',
+        '--sdk',
+        'iphoneos',
+        '--show-sdk-path',
+      ]),
+      const FakeCommand(command: <String>[
+        'xcrun',
+        '--sdk',
+        'iphoneos',
+        '--show-sdk-path',
+      ]),
+      FakeCommand(command: <String>[
+        'xcrun',
+        'cc',
+        '-arch',
+        'armv7',
+        '-isysroot',
+        '',
+        '-c',
+        '$build/armv7/snapshot_assembly.S',
+        '-o',
+        '$build/armv7/snapshot_assembly.o',
+      ]),
+      FakeCommand(command: <String>[
+        'xcrun',
+        'cc',
+        '-arch',
+        'arm64',
+        '-isysroot',
+        '',
+        '-c',
+        '$build/arm64/snapshot_assembly.S',
+        '-o',
+        '$build/arm64/snapshot_assembly.o',
+      ]),
+      FakeCommand(command: <String>[
+        'xcrun',
+        'clang',
+        '-arch',
+        'armv7',
+        '-miphoneos-version-min=8.0',
+        '-dynamiclib',
+        '-Xlinker',
+        '-rpath',
+        '-Xlinker',
+        '@executable_path/Frameworks',
+        '-Xlinker',
+        '-rpath',
+        '-Xlinker',
+        '@loader_path/Frameworks',
+        '-install_name',
+        '@rpath/App.framework/App',
+        '-isysroot',
+        '',
+        '-o',
+        '$build/armv7/App.framework/App',
+        '$build/armv7/snapshot_assembly.o',
+      ]),
+      FakeCommand(command: <String>[
+        'xcrun',
+        'clang',
+        '-arch',
+        'arm64',
+        '-miphoneos-version-min=8.0',
+        '-dynamiclib',
+        '-Xlinker',
+        '-rpath',
+        '-Xlinker',
+        '@executable_path/Frameworks',
+        '-Xlinker',
+        '-rpath',
+        '-Xlinker',
+        '@loader_path/Frameworks',
+        '-install_name',
+        '@rpath/App.framework/App',
+        '-isysroot',
+        '',
+        '-o',
+        '$build/arm64/App.framework/App',
+        '$build/arm64/snapshot_assembly.o',
+      ]),
+      FakeCommand(command: <String>[
+        'lipo',
+        '$build/armv7/App.framework/App',
+        '$build/arm64/App.framework/App',
+        '-create',
+        '-output',
+        '$build/App.framework/App',
+      ]),
+    ]);
     iosEnvironment.defines[kIosArchs] ='armv7 arm64';
-    when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async {
-      globals.fs.file(globals.fs.path.join(iosEnvironment.buildDir.path, 'App.framework', 'App'))
-          .createSync(recursive: true);
-      return FakeProcessResult(
-        stdout: '',
-        stderr: '',
-      );
-    });
-    final BuildResult result = await buildSystem
-        .build(const AotAssemblyProfile(), iosEnvironment);
-    expect(result.success, true);
-  }, overrides: <Type, Generator>{
-    ProcessManager: () => mockProcessManager,
-  }));
-
-  test('aot_assembly_profile with bitcode sends correct argument to snapshotter (one arch)', () => testbed.run(() async {
-    iosEnvironment.defines[kIosArchs] = 'arm64';
-    iosEnvironment.defines[kBitcodeFlag] = 'true';
-
-    final FakeProcessResult fakeProcessResult = FakeProcessResult(
-      stdout: '',
-      stderr: '',
-    );
-    final RunResult fakeRunResult = RunResult(fakeProcessResult, const <String>['foo']);
-    when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async {
-      globals.fs.file(globals.fs.path.join(iosEnvironment.buildDir.path, 'App.framework', 'App'))
-          .createSync(recursive: true);
-      return fakeProcessResult;
-    });
-
-    when(mockXcode.cc(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
-    when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
-
-    final BuildResult result = await buildSystem.build(const AotAssemblyProfile(), iosEnvironment);
-
-    expect(result.success, true);
-    verify(mockXcode.cc(argThat(contains('-fembed-bitcode')))).called(1);
-    verify(mockXcode.clang(argThat(contains('-fembed-bitcode')))).called(1);
-  }, overrides: <Type, Generator>{
-    ProcessManager: () => mockProcessManager,
-    Xcode: () => mockXcode,
-  }));
-
-  test('aot_assembly_profile with bitcode sends correct argument to snapshotter (mutli arch)', () => testbed.run(() async {
-    iosEnvironment.defines[kIosArchs] = 'armv7 arm64';
-    iosEnvironment.defines[kBitcodeFlag] = 'true';
-
-    final FakeProcessResult fakeProcessResult = FakeProcessResult(
-      stdout: '',
-      stderr: '',
-    );
-    final RunResult fakeRunResult = RunResult(fakeProcessResult, const <String>['foo']);
-    when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async {
-      globals.fs.file(globals.fs.path.join(iosEnvironment.buildDir.path, 'App.framework', 'App'))
-          .createSync(recursive: true);
-      return fakeProcessResult;
-    });
-
-    when(mockXcode.cc(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
-    when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
 
     await const AotAssemblyProfile().build(iosEnvironment);
 
-    verify(mockXcode.cc(argThat(contains('-fembed-bitcode')))).called(2);
-    verify(mockXcode.clang(argThat(contains('-fembed-bitcode')))).called(2);
-  }, overrides: <Type, Generator>{
-    ProcessManager: () => mockProcessManager,
-    Xcode: () => mockXcode,
+    expect(processManager.hasRemainingExpectations, false);
   }));
 
-  test('aot_assembly_profile will lipo binaries together when multiple archs are requested', () => testbed.run(() async {
-    iosEnvironment.defines[kIosArchs] = 'armv7 arm64';
-    when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async {
-      globals.fs.file(globals.fs.path.join(iosEnvironment.buildDir.path, 'App.framework', 'App'))
-          .createSync(recursive: true);
-      return FakeProcessResult(
-        stdout: '',
-        stderr: '',
-      );
-    });
-    final BuildResult result = await buildSystem.build(const AotAssemblyProfile(), iosEnvironment);
+  test('AotAssemblyProfile with bitcode sends correct argument to snapshotter (one arch)', () => testbed.run(() async {
+    iosEnvironment.defines[kIosArchs] = 'arm64';
+    iosEnvironment.defines[kBitcodeFlag] = 'true';
+    final String build = iosEnvironment.buildDir.path;
+    processManager = FakeProcessManager.list(<FakeCommand>[
+      FakeCommand(command: <String>[
+        // This path is not known by the cache due to the iOS gen_snapshot split.
+        'bin/cache/artifacts/engine/ios-profile/gen_snapshot_arm64',
+        '--deterministic',
+        kAssemblyAot,
+        '--assembly=$build/arm64/snapshot_assembly.S',
+        '--strip',
+        '--no-causal-async-stacks',
+        '--lazy-async-stacks',
+        '$build/app.dill',
+      ]),
+      const FakeCommand(command: <String>[
+        'xcrun',
+        '--sdk',
+        'iphoneos',
+        '--show-sdk-path',
+      ]),
+      FakeCommand(command: <String>[
+        'xcrun',
+        'cc',
+        '-arch',
+        'arm64',
+        '-isysroot',
+        '',
+        // Contains bitcode flag.
+        '-fembed-bitcode',
+        '-c',
+        '$build/arm64/snapshot_assembly.S',
+        '-o',
+        '$build/arm64/snapshot_assembly.o',
+      ]),
+      FakeCommand(command: <String>[
+        'xcrun',
+        'clang',
+        '-arch',
+        'arm64',
+        '-miphoneos-version-min=8.0',
+        '-dynamiclib',
+        '-Xlinker',
+        '-rpath',
+        '-Xlinker',
+        '@executable_path/Frameworks',
+        '-Xlinker',
+        '-rpath',
+        '-Xlinker',
+        '@loader_path/Frameworks',
+        '-install_name',
+        '@rpath/App.framework/App',
+        // Contains bitcode flag.
+        '-fembed-bitcode',
+        '-isysroot',
+        '',
+        '-o',
+        '$build/arm64/App.framework/App',
+        '$build/arm64/snapshot_assembly.o',
+      ]),
+      FakeCommand(command: <String>[
+        'lipo',
+        '$build/arm64/App.framework/App',
+        '-create',
+        '-output',
+        '$build/App.framework/App',
+      ]),
+    ]);
 
-    expect(result.success, true);
-  }, overrides: <Type, Generator>{
-    ProcessManager: () => mockProcessManager,
+    await const AotAssemblyProfile().build(iosEnvironment);
+
+    expect(processManager.hasRemainingExpectations, false);
   }));
 
   test('kExtraGenSnapshotOptions passes values to gen_snapshot', () => testbed.run(() async {
     androidEnvironment.defines[kExtraGenSnapshotOptions] = 'foo,bar,baz=2';
+    androidEnvironment.defines[kBuildMode] = getNameForBuildMode(BuildMode.profile);
+    final String build = androidEnvironment.buildDir.path;
 
-    when(genSnapshot.run(
-      snapshotType: anyNamed('snapshotType'),
-      darwinArch: anyNamed('darwinArch'),
-      additionalArgs: captureAnyNamed('additionalArgs'),
-    )).thenAnswer((Invocation invocation) async {
-      expect(invocation.namedArguments[#additionalArgs], containsAll(<String>[
+    processManager = FakeProcessManager.list(<FakeCommand>[
+      FakeCommand(command: <String>[
+        artifacts.getArtifactPath(Artifact.genSnapshot, mode: BuildMode.profile),
+        '--deterministic',
         'foo',
         'bar',
         'baz=2',
-      ]));
-      return 0;
-    });
-
+        kElfAot,
+        '--elf=$build/app.so',
+        '--strip',
+        '--no-sim-use-hardfp',
+        '--no-use-integer-division',
+        '--no-causal-async-stacks',
+        '--lazy-async-stacks',
+        '$build/app.dill',
+      ]),
+    ]);
 
     await const AotElfRelease().build(androidEnvironment);
-  }, overrides: <Type, Generator>{
-    GenSnapshot: () => MockGenSnapshot(),
+
+    expect(processManager.hasRemainingExpectations, false);
   }));
 }
-
-class MockProcessManager extends Mock implements ProcessManager {}
-class MockGenSnapshot extends Mock implements GenSnapshot {}
-class MockXcode extends Mock implements Xcode {}
-
-class FakeGenSnapshot implements GenSnapshot {
-  List<String> lastCallAdditionalArgs;
-  @override
-  Future<int> run({SnapshotType snapshotType, DarwinArch darwinArch, Iterable<String> additionalArgs = const <String>[]}) async {
-    lastCallAdditionalArgs = additionalArgs.toList();
-    final Directory out = globals.fs.file(lastCallAdditionalArgs.last).parent;
-    if (darwinArch == null) {
-      out.childFile('app.so').createSync();
-      out.childFile('gen_snapshot.d').createSync();
-      return 0;
-    }
-    out.childDirectory('App.framework').childFile('App').createSync(recursive: true);
-
-    final String assembly = lastCallAdditionalArgs
-        .firstWhere((String arg) => arg.startsWith('--assembly'))
-        .substring('--assembly='.length);
-    globals.fs.file(assembly).createSync();
-    globals.fs.file(assembly.replaceAll('.S', '.o')).createSync();
-    return 0;
-  }
-}
-
-class FakeKernelCompilerFactory implements KernelCompilerFactory {
-  KernelCompiler kernelCompiler = FakeKernelCompiler();
-
-  @override
-  Future<KernelCompiler> create(FlutterProject flutterProject) async {
-    return kernelCompiler;
-  }
-}
-
-class FakeKernelCompiler implements KernelCompiler {
-  @override
-  Future<CompilerOutput> compile({
-    String sdkRoot,
-    String mainPath,
-    String outputFilePath,
-    String depFilePath,
-    TargetModel targetModel = TargetModel.flutter,
-    bool linkPlatformKernelIn = false,
-    bool aot = false,
-    BuildMode buildMode,
-    bool causalAsyncStacks = true,
-    bool trackWidgetCreation,
-    List<String> extraFrontEndOptions,
-    String packagesPath,
-    List<String> fileSystemRoots,
-    String fileSystemScheme,
-    String platformDill,
-    String initializeFromDill,
-    List<String> dartDefines,
-  }) async {
-    globals.fs.file(outputFilePath).createSync(recursive: true);
-    return CompilerOutput(outputFilePath, 0, null);
-  }
-}
-
-class MockKernelCompilerFactory extends Mock implements KernelCompilerFactory {}
-class MockKernelCompiler extends Mock implements KernelCompiler {}
diff --git a/packages/flutter_tools/test/src/fake_process_manager.dart b/packages/flutter_tools/test/src/fake_process_manager.dart
index 0077547..c31511d 100644
--- a/packages/flutter_tools/test/src/fake_process_manager.dart
+++ b/packages/flutter_tools/test/src/fake_process_manager.dart
@@ -202,6 +202,11 @@
   /// This is a no-op on [FakeProcessManager.any].
   void addCommand(FakeCommand command);
 
+  /// Whether this fake has more [FakeCommand]s that are expected to run.
+  ///
+  /// This is always `true` for [FakeProcessManager.any].
+  bool get hasRemainingExpectations;
+
   @protected
   FakeCommand findCommand(List<String> command, String workingDirectory, Map<String, String> environment);
 
@@ -299,6 +304,9 @@
 
   @override
   void addCommand(FakeCommand command) { }
+
+  @override
+  bool get hasRemainingExpectations => true;
 }
 
 class _SequenceProcessManager extends FakeProcessManager {
@@ -325,4 +333,7 @@
   void addCommand(FakeCommand command) {
     _commands.add(command);
   }
+
+  @override
+  bool get hasRemainingExpectations => _commands.isNotEmpty;
 }