Unverified Commit 2c94f2b4 authored by Zachary Anderson's avatar Zachary Anderson Committed by GitHub

[flutter_tool] Kill a timing out process before trying to drain its stdout/err streams (#40159)

parent e6ae95c4
......@@ -275,13 +275,23 @@ Future<RunResult> runAsync(
int exitCode;
exitCode = await process.exitCode.timeout(timeout, onTimeout: () {
// The process timed out. Kill it.
processManager.killPid(process.pid);
return null;
});
String stdoutString;
String stderrString;
try {
await Future.wait<void>(<Future<void>>[stdoutFuture, stderrFuture]);
Future<void> stdioFuture =
Future.wait<void>(<Future<void>>[stdoutFuture, stderrFuture]);
if (exitCode == null) {
// If we had to kill the process for a timeout, only wait a short time
// for the stdio streams to drain in case killing the process didn't
// work.
stdioFuture = stdioFuture.timeout(const Duration(seconds: 1));
}
await stdioFuture;
} catch (_) {
// Ignore errors on the process' stdout and stderr streams. Just capture
// whatever we got, and use the exit code
......@@ -299,9 +309,6 @@ Future<RunResult> runAsync(
return runResult;
}
// The process timed out. Kill it.
processManager.killPid(process.pid);
// If we are out of timeoutRetries, throw a ProcessException.
if (timeoutRetries < 0) {
throw ProcessException(cmd[0], cmd.sublist(1),
......
......@@ -2,6 +2,8 @@
// 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_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart';
......@@ -103,13 +105,13 @@ void main() {
// MockProcessManager has an implementation of start() that returns the
// result of processFactory.
flakyProcessManager = MockProcessManager();
});
testUsingContext('flaky process fails without retry', () async {
flakyProcessManager.processFactory = flakyProcessFactory(
flakes: 1,
delay: delay,
);
});
testUsingContext('flaky process fails without retry', () async {
final RunResult result = await runAsync(
<String>['dummy'],
timeout: delay + const Duration(seconds: 1),
......@@ -120,6 +122,10 @@ void main() {
});
testUsingContext('flaky process succeeds with retry', () async {
flakyProcessManager.processFactory = flakyProcessFactory(
flakes: 1,
delay: delay,
);
final RunResult result = await runAsync(
<String>['dummy'],
timeout: delay - const Duration(milliseconds: 500),
......@@ -131,6 +137,22 @@ void main() {
});
testUsingContext('flaky process generates ProcessException on timeout', () async {
final Completer<List<int>> flakyStderr = Completer<List<int>>();
final Completer<List<int>> flakyStdout = Completer<List<int>>();
flakyProcessManager.processFactory = flakyProcessFactory(
flakes: 1,
delay: delay,
stderr: () => Stream<List<int>>.fromFuture(flakyStderr.future),
stdout: () => Stream<List<int>>.fromFuture(flakyStdout.future),
);
when(flakyProcessManager.killPid(any)).thenAnswer((_) {
// Don't let the stderr stream stop until the process is killed. This
// ensures that runAsync() does not delay killing the process until
// stdout and stderr are drained (which won't happen).
flakyStderr.complete(<int>[]);
flakyStdout.complete(<int>[]);
return true;
});
expect(() async => await runAsync(
<String>['dummy'],
timeout: delay - const Duration(milliseconds: 500),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment