[flutter_tools] add debugging to ios/core_devices.dart (#142187)

Add debugging for #141892 to detect when the temp file mysteriously
disappears after running devicectl.
diff --git a/packages/flutter_tools/lib/src/ios/core_devices.dart b/packages/flutter_tools/lib/src/ios/core_devices.dart
index 1e49950..71a6526 100644
--- a/packages/flutter_tools/lib/src/ios/core_devices.dart
+++ b/packages/flutter_tools/lib/src/ios/core_devices.dart
@@ -78,8 +78,16 @@
     ];
 
     try {
-      await _processUtils.run(command, throwOnError: true);
+      final RunResult result = await _processUtils.run(command, throwOnError: true);
 
+      if (!output.existsSync()) {
+        _logger.printError('After running the command ${command.join(' ')} the file');
+        _logger.printError('${output.path} was expected to exist, but it did not.');
+        _logger.printError('The process exited with code ${result.exitCode} and');
+        _logger.printError('Stdout:\n\n${result.stdout.trim()}\n');
+        _logger.printError('Stderr:\n\n${result.stderr.trim()}');
+        throw StateError('Expected the file ${output.path} to exist but it did not');
+      }
       final String stringOutput = output.readAsStringSync();
       _logger.printTrace(stringOutput);
 
diff --git a/packages/flutter_tools/test/general.shard/ios/core_devices_test.dart b/packages/flutter_tools/test/general.shard/ios/core_devices_test.dart
index 1313b42..860947c 100644
--- a/packages/flutter_tools/test/general.shard/ios/core_devices_test.dart
+++ b/packages/flutter_tools/test/general.shard/ios/core_devices_test.dart
@@ -87,7 +87,7 @@
     setUp(() {
       logger = BufferLogger.test();
       fakeProcessManager = FakeProcessManager.empty();
-      // TODO(fujino): make this FakeProcessManager.empty()
+      // TODO(fujino): re-use fakeProcessManager
       xcode = Xcode.test(processManager: FakeProcessManager.any());
       deviceControl = IOSCoreDeviceControl(
         logger: logger,
@@ -1355,6 +1355,51 @@
         expect(fakeProcessManager, hasNoRemainingExpectations);
       });
 
+      testWithoutContext('Handles json file mysteriously disappearing', () async {
+        final Directory tempDir = fileSystem.systemTempDirectory
+            .childDirectory('core_devices.rand0');
+        final File tempFile = tempDir.childFile('core_device_list.json');
+        final List<String> args = <String>[
+          'xcrun',
+          'devicectl',
+          'list',
+          'devices',
+          '--timeout',
+          '5',
+          '--json-output',
+          tempFile.path,
+        ];
+        fakeProcessManager.addCommand(FakeCommand(
+          command: args,
+          onRun: (_) {
+            // Simulate that this command deleted tempFile, did not create a
+            // new one, and exited successfully
+            expect(tempFile, exists);
+            tempFile.deleteSync();
+            expect(tempFile, isNot(exists));
+          },
+        ));
+
+        await expectLater(
+          () => deviceControl.getCoreDevices(),
+          throwsA(
+            isA<StateError>().having(
+              (StateError e) => e.message,
+              'message',
+              contains('Expected the file ${tempFile.path} to exist but it did not'),
+            ),
+          ),
+        );
+        expect(
+          logger.errorText,
+          contains('After running the command xcrun devicectl list devices '
+            '--timeout 5 --json-output ${tempFile.path} the file\n'
+            '${tempFile.path} was expected to exist, but it did not',
+          ),
+        );
+        expect(fakeProcessManager, hasNoRemainingExpectations);
+      });
+
       testWithoutContext('No devices', () async {
         const String deviceControlOutput = '''
 {
diff --git a/packages/flutter_tools/test/integration.shard/overall_experience_test.dart b/packages/flutter_tools/test/integration.shard/overall_experience_test.dart
index 8cc4fff..eddf735 100644
--- a/packages/flutter_tools/test/integration.shard/overall_experience_test.dart
+++ b/packages/flutter_tools/test/integration.shard/overall_experience_test.dart
@@ -66,15 +66,25 @@
       tryToDelete(fileSystem.directory(tempDirectory));
     }
   }, skip: Platform.isWindows); // [intended] Windows doesn't support sending signals so we don't care if it can store the PID.
-  testWithoutContext('flutter run handle SIGUSR1/2', () async {
+
+  testWithoutContext('flutter run handle SIGUSR1/2 run', () async {
     final String tempDirectory = fileSystem.systemTempDirectory.createTempSync('flutter_overall_experience_test.').resolveSymbolicLinksSync();
     final String pidFile = fileSystem.path.join(tempDirectory, 'flutter.pid');
     final String testDirectory = fileSystem.path.join(flutterRoot, 'dev', 'integration_tests', 'ui');
     final String testScript = fileSystem.path.join('lib', 'commands.dart');
     late int pid;
+    final List<String> command = <String>[
+      'run',
+      '-dflutter-tester',
+      '--report-ready',
+      '--pid-file',
+      pidFile,
+      '--no-devtools',
+      testScript,
+    ];
     try {
       final ProcessTestResult result = await runFlutter(
-        <String>['run', '-dflutter-tester', '--report-ready', '--pid-file', pidFile, '--no-devtools', testScript],
+        command,
         testDirectory,
         <Transition>[
           Multiple(<Pattern>['Flutter run key commands.', 'called paint'], handler: (String line) {
@@ -108,16 +118,22 @@
         'called main',
         'called paint',
       ]);
-      expect(result.stdout.where((String line) => !line.startsWith('called ')), <Object>[
-        // logs start after we receive the response to sending SIGUSR1
-        'Performing hot reload...'.padRight(progressMessageWidth),
-        startsWith('Reloaded 0 libraries in '),
-        'Performing hot restart...'.padRight(progressMessageWidth),
-        startsWith('Restarted application in '),
-        '', // this newline is the one for after we hit "q"
-        'Application finished.',
-        'ready',
-      ]);
+      expect(
+        result.stdout.where((String line) => !line.startsWith('called ')),
+        <Object>[
+          // logs start after we receive the response to sending SIGUSR1
+          'Performing hot reload...'.padRight(progressMessageWidth),
+          startsWith('Reloaded 0 libraries in '),
+          'Performing hot restart...'.padRight(progressMessageWidth),
+          startsWith('Restarted application in '),
+          '', // this newline is the one for after we hit "q"
+          'Application finished.',
+          'ready',
+        ],
+        reason: 'stdout from command ${command.join(' ')} was unexpected, '
+          'full Stdout:\n\n${result.stdout.join('\n')}\n\n'
+          'Stderr:\n\n${result.stderr.join('\n')}',
+      );
       expect(result.exitCode, 0);
     } finally {
       tryToDelete(fileSystem.directory(tempDirectory));
@@ -128,9 +144,16 @@
     final String tempDirectory = fileSystem.systemTempDirectory.createTempSync('flutter_overall_experience_test.').resolveSymbolicLinksSync();
     final String testDirectory = fileSystem.path.join(flutterRoot, 'dev', 'integration_tests', 'ui');
     final String testScript = fileSystem.path.join('lib', 'commands.dart');
+    final List<String> command = <String>[
+      'run',
+      '-dflutter-tester',
+      '--report-ready',
+      '--no-devtools',
+      testScript,
+    ];
     try {
       final ProcessTestResult result = await runFlutter(
-        <String>['run', '-dflutter-tester', '--report-ready', '--no-devtools', testScript],
+        command,
         testDirectory,
         <Transition>[
           Multiple(<Pattern>['Flutter run key commands.', 'called main', 'called paint'], handler: (String line) {
@@ -171,23 +194,28 @@
         // debugPaintSizeEnabled = false:
         'called paint',
       ]);
-      expect(result.stdout.where((String line) => !line.startsWith('called ')), <Object>[
-        // logs start after we receive the response to hitting "r"
-        'Performing hot reload...'.padRight(progressMessageWidth),
-        startsWith('Reloaded 0 libraries in '),
-        'ready',
-        '', // this newline is the one for after we hit "R"
-        'Performing hot restart...'.padRight(progressMessageWidth),
-        startsWith('Restarted application in '),
-        'ready',
-        '', // newline for after we hit "p" the first time
-        'ready',
-        '', // newline for after we hit "p" the second time
-        'ready',
-        '', // this newline is the one for after we hit "q"
-        'Application finished.',
-        'ready',
-      ]);
+      expect(
+        result.stdout.where((String line) => !line.startsWith('called ')), <Object>[
+          // logs start after we receive the response to hitting "r"
+          'Performing hot reload...'.padRight(progressMessageWidth),
+          startsWith('Reloaded 0 libraries in '),
+          'ready',
+          '', // this newline is the one for after we hit "R"
+          'Performing hot restart...'.padRight(progressMessageWidth),
+          startsWith('Restarted application in '),
+          'ready',
+          '', // newline for after we hit "p" the first time
+          'ready',
+          '', // newline for after we hit "p" the second time
+          'ready',
+          '', // this newline is the one for after we hit "q"
+          'Application finished.',
+          'ready',
+        ],
+        reason: 'stdout from command ${command.join(' ')} was unexpected, '
+          'full Stdout:\n\n${result.stdout.join('\n')}\n\n'
+          'Stderr:\n\n${result.stderr.join('\n')}',
+      );
       expect(result.exitCode, 0);
     } finally {
       tryToDelete(fileSystem.directory(tempDirectory));
diff --git a/packages/flutter_tools/test/integration.shard/transition_test_utils.dart b/packages/flutter_tools/test/integration.shard/transition_test_utils.dart
index fa3a7f3..3b187f2 100644
--- a/packages/flutter_tools/test/integration.shard/transition_test_utils.dart
+++ b/packages/flutter_tools/test/integration.shard/transition_test_utils.dart
@@ -175,8 +175,8 @@
   bool debug = false,
   bool logging = true,
   Duration expectedMaxDuration = const Duration(
-      minutes:
-          10), // must be less than test timeout of 15 minutes! See ../../dart_test.yaml.
+    minutes: 10,
+  ), // must be less than test timeout of 15 minutes! See ../../dart_test.yaml.
 }) async {
   const LocalPlatform platform = LocalPlatform();
   final Stopwatch clock = Stopwatch()..start();