Resume isolate before terminating tests to prevent flutter_tester leaks in integration tests (#45248)

* Resume isolate before terminating tests to prevent flutter_tester leak

* Fix lint

* Catch exceptions from resume() as well as timeout()

* Formatting fixes

* Don't try to resume if there's no vm service

* Fix handling of timeouts to not leak futures
diff --git a/packages/flutter_tools/test/integration.shard/test_driver.dart b/packages/flutter_tools/test/integration.shard/test_driver.dart
index 314a479..4fb07e3 100644
--- a/packages/flutter_tools/test/integration.shard/test_driver.dart
+++ b/packages/flutter_tools/test/integration.shard/test_driver.dart
@@ -164,6 +164,12 @@
     if (_processPid == null) {
       return -1;
     }
+    // If we try to kill the process while it's paused, we'll end up terminating
+    // it forcefully and it won't terminate child processes, so we need to ensure
+    // it's running before terminating.
+    await resume().timeout(defaultTimeout)
+        .catchError((Object e) => _debugPrint('Ignoring failure to resume during shutdown'));
+
     _debugPrint('Sending SIGTERM to $_processPid..');
     ProcessSignal.SIGTERM.send(_processPid);
     return _process.exitCode.timeout(quitTimeout, onTimeout: _killForcefully);
@@ -345,7 +351,7 @@
         return;
       }
       if ((event != null && json['event'] == event) ||
-          (id    != null && json['id']    == id)) {
+          (id != null && json['id'] == id)) {
         await subscription.cancel();
         _debugPrint('OK ($interestingOccurrence)');
         response.complete(json);
@@ -380,10 +386,8 @@
 
     if (_printDebugOutputToStdOut) {
       _debugPrint('$task...');
-      return callback()..timeout(timeout, onTimeout: () {
-        _debugPrint('$task is taking longer than usual...');
-        return null;
-      });
+      final Timer longWarning = Timer(timeout, () => _debugPrint('$task is taking longer than usual...'));
+      return callback().whenComplete(longWarning.cancel);
     }
 
     // We're not showing all output to the screen, so let's capture the output
@@ -399,14 +403,12 @@
     }
     final StreamSubscription<String> subscription = _allMessages.stream.listen(logMessage);
 
-    final Future<T> future = callback();
-
-    future.timeout(timeout ?? defaultTimeout, onTimeout: () {
+    final Timer longWarning = Timer(timeout, () {
       _debugPrint(messages.toString());
       timeoutExpired = true;
       _debugPrint('$task is taking longer than usual...');
-      return null;
     });
+    final Future<T> future = callback().whenComplete(longWarning.cancel);
 
     return future.catchError((dynamic error) {
       if (!timeoutExpired) {
@@ -525,7 +527,7 @@
         // have already completed.
         _currentRunningAppId = (await started)['params']['appId'] as String;
         prematureExitGuard.complete();
-      } catch(error, stackTrace) {
+      } catch (error, stackTrace) {
         prematureExitGuard.completeError(error, stackTrace);
       }
     }());
@@ -546,7 +548,7 @@
       'app.restart',
       <String, dynamic>{'appId': _currentRunningAppId, 'fullRestart': fullRestart, 'pause': pause},
     );
-    _debugPrint('${ fullRestart ? "Hot restart" : "Hot reload" } complete.');
+    _debugPrint('${fullRestart ? "Hot restart" : "Hot reload"} complete.');
 
     if (hotReloadResponse == null || hotReloadResponse['code'] != 0) {
       _throwErrorResponse('Hot ${fullRestart ? 'restart' : 'reload'} request failed');