New flag to `flutter drive` to skip installing fresh app on device (#30818)

* add a --build/--no-build flag to flutter drive command
diff --git a/packages/flutter_tools/lib/src/commands/drive.dart b/packages/flutter_tools/lib/src/commands/drive.dart
index 11df0d5..22232ab 100644
--- a/packages/flutter_tools/lib/src/commands/drive.dart
+++ b/packages/flutter_tools/lib/src/commands/drive.dart
@@ -64,6 +64,10 @@
               'extension, so e.g. if the target is "lib/main.dart", the driver will be '
               '"test_driver/main_test.dart".',
         valueHelp: 'path',
+      )
+      ..addFlag('build',
+        defaultsTo: true,
+        help: 'Build the app before running.',
       );
   }
 
@@ -78,6 +82,7 @@
 
   Device _device;
   Device get device => _device;
+  bool get shouldBuild => argResults['build'];
 
   /// Subscription to log messages printed on the device or simulator.
   // ignore: cancel_subscriptions
@@ -234,12 +239,15 @@
   printTrace('Stopping previously running application, if any.');
   await appStopper(command);
 
-  printTrace('Installing application package.');
   final ApplicationPackage package = await command.applicationPackages
       .getPackageForPlatform(await command.device.targetPlatform);
-  if (await command.device.isAppInstalled(package))
-    await command.device.uninstallApp(package);
-  await command.device.installApp(package);
+
+  if (command.shouldBuild) {
+    printTrace('Installing application package.');
+    if (await command.device.isAppInstalled(package))
+      await command.device.uninstallApp(package);
+    await command.device.installApp(package);
+  }
 
   final Map<String, dynamic> platformArgs = <String, dynamic>{};
   if (command.traceStartup)
@@ -264,6 +272,7 @@
       observatoryPort: command.observatoryPort,
     ),
     platformArgs: platformArgs,
+    prebuiltApplication: !command.shouldBuild,
     usesTerminalUi: false,
   );
 
diff --git a/packages/flutter_tools/test/commands/drive_test.dart b/packages/flutter_tools/test/commands/drive_test.dart
index 81a435c..d7bf21b 100644
--- a/packages/flutter_tools/test/commands/drive_test.dart
+++ b/packages/flutter_tools/test/commands/drive_test.dart
@@ -291,6 +291,130 @@
         Platform: macOsPlatform,
       });
     });
+
+    group('build arguments', () {
+      String testApp, testFile;
+
+      setUp(() {
+        restoreAppStarter();
+      });
+
+      Future<void> appStarterSetup() async {
+        withMockDevice();
+
+        final MockDeviceLogReader mockDeviceLogReader = MockDeviceLogReader();
+        when(mockDevice.getLogReader()).thenReturn(mockDeviceLogReader);
+        final MockLaunchResult mockLaunchResult = MockLaunchResult();
+        when(mockLaunchResult.started).thenReturn(true);
+        when(mockDevice.startApp(
+            null,
+            mainPath: anyNamed('mainPath'),
+            route: anyNamed('route'),
+            debuggingOptions: anyNamed('debuggingOptions'),
+            platformArgs: anyNamed('platformArgs'),
+            prebuiltApplication: anyNamed('prebuiltApplication'),
+            usesTerminalUi: false,
+        )).thenAnswer((_) => Future<LaunchResult>.value(mockLaunchResult));
+        when(mockDevice.isAppInstalled(any)).thenAnswer((_) => Future<bool>.value(false));
+
+        testApp = fs.path.join(tempDir.path, 'test', 'e2e.dart');
+        testFile = fs.path.join(tempDir.path, 'test_driver', 'e2e_test.dart');
+
+        testRunner = (List<String> testArgs, String observatoryUri) async {
+          throwToolExit(null, exitCode: 123);
+        };
+        appStopper = expectAsync1(
+            (DriveCommand command) async {
+              return true;
+            },
+            count: 2,
+        );
+
+        final MemoryFileSystem memFs = fs;
+        await memFs.file(testApp).writeAsString('main() {}');
+        await memFs.file(testFile).writeAsString('main() {}');
+      }
+
+      testUsingContext('does not use pre-built app if no build arg provided', () async {
+        await appStarterSetup();
+
+        final List<String> args = <String>[
+          'drive',
+          '--target=$testApp',
+        ];
+        try {
+          await createTestCommandRunner(command).run(args);
+        } on ToolExit catch (e) {
+          expect(e.exitCode, 123);
+          expect(e.message, null);
+        }
+        verify(mockDevice.startApp(
+                null,
+                mainPath: anyNamed('mainPath'),
+                route: anyNamed('route'),
+                debuggingOptions: anyNamed('debuggingOptions'),
+                platformArgs: anyNamed('platformArgs'),
+                prebuiltApplication: false,
+                usesTerminalUi: false,
+        ));
+      }, overrides: <Type, Generator>{
+        FileSystem: () => fs,
+      });
+
+      testUsingContext('does not use pre-built app if --build arg provided', () async {
+        await appStarterSetup();
+
+        final List<String> args = <String>[
+          'drive',
+          '--build',
+          '--target=$testApp',
+        ];
+        try {
+          await createTestCommandRunner(command).run(args);
+        } on ToolExit catch (e) {
+          expect(e.exitCode, 123);
+          expect(e.message, null);
+        }
+        verify(mockDevice.startApp(
+                null,
+                mainPath: anyNamed('mainPath'),
+                route: anyNamed('route'),
+                debuggingOptions: anyNamed('debuggingOptions'),
+                platformArgs: anyNamed('platformArgs'),
+                prebuiltApplication: false,
+                usesTerminalUi: false,
+        ));
+      }, overrides: <Type, Generator>{
+        FileSystem: () => fs,
+      });
+
+      testUsingContext('uses prebuilt app if --no-build arg provided', () async {
+        await appStarterSetup();
+
+        final List<String> args = <String>[
+          'drive',
+          '--no-build',
+          '--target=$testApp',
+        ];
+        try {
+          await createTestCommandRunner(command).run(args);
+        } on ToolExit catch (e) {
+          expect(e.exitCode, 123);
+          expect(e.message, null);
+        }
+        verify(mockDevice.startApp(
+                null,
+                mainPath: anyNamed('mainPath'),
+                route: anyNamed('route'),
+                debuggingOptions: anyNamed('debuggingOptions'),
+                platformArgs: anyNamed('platformArgs'),
+                prebuiltApplication: true,
+                usesTerminalUi: false,
+        ));
+      }, overrides: <Type, Generator>{
+        FileSystem: () => fs,
+      });
+    });
   });
 }
 
@@ -301,3 +425,5 @@
 }
 
 class MockAndroidDevice extends Mock implements AndroidDevice { }
+
+class MockLaunchResult extends Mock implements LaunchResult { }