Add macOS hot reload test (#45264)

diff --git a/dev/devicelab/bin/tasks/hot_mode_dev_cycle_macos_target__benchmark.dart b/dev/devicelab/bin/tasks/hot_mode_dev_cycle_macos_target__benchmark.dart
new file mode 100644
index 0000000..59e1379
--- /dev/null
+++ b/dev/devicelab/bin/tasks/hot_mode_dev_cycle_macos_target__benchmark.dart
@@ -0,0 +1,14 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:flutter_devicelab/tasks/hot_mode_tests.dart';
+import 'package:flutter_devicelab/framework/framework.dart';
+
+Future<void> main() async {
+  await task(createHotModeTest(deviceIdOverride: 'macos', environment: <String, String>{
+    'FLUTTER_MACOS': 'true',
+  }));
+}
diff --git a/dev/devicelab/lib/framework/utils.dart b/dev/devicelab/lib/framework/utils.dart
index f132c0d..0ac79c6 100644
--- a/dev/devicelab/lib/framework/utils.dart
+++ b/dev/devicelab/lib/framework/utils.dart
@@ -121,7 +121,7 @@
 
   for (FileSystemEntity entity in source.listSync(followLinks: false)) {
     final String name = path.basename(entity.path);
-    if (entity is Directory)
+    if (entity is Directory && !entity.path.contains('.dart_tool'))
       recursiveCopy(entity, Directory(path.join(target.path, name)));
     else if (entity is File) {
       final File dest = File(path.join(target.path, name));
diff --git a/dev/devicelab/lib/tasks/hot_mode_tests.dart b/dev/devicelab/lib/tasks/hot_mode_tests.dart
index 166d03c..711a1fd 100644
--- a/dev/devicelab/lib/tasks/hot_mode_tests.dart
+++ b/dev/devicelab/lib/tasks/hot_mode_tests.dart
@@ -15,14 +15,17 @@
 final Directory _editedFlutterGalleryDir = dir(path.join(Directory.systemTemp.path, 'edited_flutter_gallery'));
 final Directory flutterGalleryDir = dir(path.join(flutterDirectory.path, 'examples/flutter_gallery'));
 
-TaskFunction createHotModeTest() {
+TaskFunction createHotModeTest({String deviceIdOverride, Map<String, String> environment}) {
   return () async {
-    final Device device = await devices.workingDevice;
-    await device.unlock();
+    if (deviceIdOverride == null) {
+      final Device device = await devices.workingDevice;
+      await device.unlock();
+      deviceIdOverride = device.deviceId;
+    }
     final File benchmarkFile = file(path.join(_editedFlutterGalleryDir.path, 'hot_benchmark.json'));
     rm(benchmarkFile);
     final List<String> options = <String>[
-      '--hot', '-d', device.deviceId, '--benchmark', '--verbose', '--resident', '--output-dill', path.join('build', 'app.dill')
+      '--hot', '-d', deviceIdOverride, '--benchmark', '--verbose', '--resident', '--output-dill', path.join('build', 'app.dill')
     ];
     int hotReloadCount = 0;
     Map<String, dynamic> twoReloadsData;
@@ -33,10 +36,17 @@
       recursiveCopy(flutterGalleryDir, _editedFlutterGalleryDir);
       await inDirectory<void>(_editedFlutterGalleryDir, () async {
         {
+          final Process clearProcess = await startProcess(
+              path.join(flutterDirectory.path, 'bin', 'flutter'),
+              flutterCommandArgs('clean', <String>[]),
+              environment: environment,
+          );
+          await clearProcess.exitCode;
+
           final Process process = await startProcess(
               path.join(flutterDirectory.path, 'bin', 'flutter'),
               flutterCommandArgs('run', options),
-              environment: null,
+              environment: environment,
           );
 
           final Completer<void> stdoutDone = Completer<void>();
@@ -90,7 +100,7 @@
           final Process process = await startProcess(
               path.join(flutterDirectory.path, 'bin', 'flutter'),
               flutterCommandArgs('run', options),
-              environment: null,
+              environment: environment,
           );
           final Completer<void> stdoutDone = Completer<void>();
           final Completer<void> stderrDone = Completer<void>();
diff --git a/dev/devicelab/manifest.yaml b/dev/devicelab/manifest.yaml
index ab4a895..ff8b773 100644
--- a/dev/devicelab/manifest.yaml
+++ b/dev/devicelab/manifest.yaml
@@ -566,6 +566,14 @@
     stage: devicelab
     required_agent_capabilities: ["mac-catalina/android"]
 
+  # macOS target platform tests
+  hot_mode_dev_cycle_macos_target__benchmark:
+    description: >
+      Checks the functionality and performance of hot reload on a macOS target platform
+    stage: devicelab
+    required_agent_capabilities: ["mac/ios"]
+    flaky: true
+
   # Tests running on Windows host
 
   flavors_test_win: