[infra] Front-end unit tests: Check for timeout of each suite separately

This CL also improves the printed messages to include full suite name
and timings.

Change-Id: I7e2129fb27a91e6ab56269f0ef4ed2c80bf4e786
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/122788
Commit-Queue: Karl Klose <karlklose@google.com>
Reviewed-by: Jonas Termansen <sortie@google.com>
This commit is contained in:
Karl Klose
2019-10-25 07:23:14 +00:00
committed by commit-bot@chromium.org
parent 87fa4e49d7
commit 81a2925ac2
5 changed files with 53 additions and 26 deletions
+43 -21
View File
@@ -4,7 +4,7 @@
import 'dart:async' show Timer;
import 'dart:convert' show jsonEncode;
import 'dart:io' show File, Platform, exit, exitCode;
import 'dart:io' show File, Platform, exitCode;
import 'dart:isolate' show Isolate, ReceivePort, SendPort;
import 'package:args/args.dart' show ArgParser;
@@ -96,7 +96,10 @@ class ResultLogger implements Logger {
TestDescription description, Step step) {}
@override
void logSuiteComplete() {}
void logSuiteStarted(testing.Suite suite) {}
@override
void logSuiteComplete(testing.Suite suite) {}
handleTestResult(TestDescription testDescription, Result result,
String fullSuiteName, bool matchedExpectations) {
@@ -252,36 +255,49 @@ main([List<String> arguments = const <String>[]]) async {
List<String> results = [];
List<String> logs = [];
Options options = Options.parse(arguments);
Timer timer = Timer(timeoutDuration, () {
// TODO(karlklose): use timer for each suite.
// TODO(karlklose): report timeout on specific tests
print("Error: Test suite timed out!");
exit(1);
});
ReceivePort resultsPort = new ReceivePort()
..listen((resultEntry) => results.add(resultEntry));
ReceivePort logsPort = new ReceivePort()
..listen((logEntry) => logs.add(logEntry));
List<Future> futures = [];
List<Future<bool>> futures = [];
// Run test suites and record the results and possible failure logs.
for (Suite suite in suites) {
// Start the test suite in a new isolate.
ReceivePort exitPort = new ReceivePort();
String name = suite.name;
SuiteConfiguration configuration = SuiteConfiguration(
suite.name,
name,
resultsPort.sendPort,
logsPort.sendPort,
options.verbose,
options.configurationName);
// TODO(karlklose): Implement --filter to select tests to run
// to implement deflaking (dartbug.com/38607).
await Isolate.spawn<SuiteConfiguration>(runSuite, configuration,
onExit: exitPort.sendPort);
futures.add(exitPort.first);
Future future = Future<bool>(() async {
Stopwatch stopwatch = Stopwatch()..start();
print("Running suite $name");
// TODO(karlklose): Implement --filter to select tests to run
// to implement deflaking (dartbug.com/38607).
Isolate isolate = await Isolate.spawn<SuiteConfiguration>(
runSuite, configuration,
onExit: exitPort.sendPort);
bool timedOut = false;
Timer timer = Timer(timeoutDuration, () {
timedOut = true;
print("Suite $name timed out after "
"${timeoutDuration.inMilliseconds}ms");
isolate.kill(priority: Isolate.immediate);
});
await exitPort.first;
timer.cancel();
if (!timedOut) {
print(
"Suite $name finished (took ${stopwatch.elapsedMilliseconds}ms).");
}
return timedOut;
});
futures.add(future);
}
// Wait for isolates to terminate and clean up.
await Future.wait(futures);
timer.cancel();
Iterable<bool> timeouts = await Future.wait(futures);
resultsPort.close();
logsPort.close();
// Write results.json and logs.json.
@@ -291,8 +307,14 @@ main([List<String> arguments = const <String>[]]) async {
await writeLinesToFile(logsJsonUri, logs);
print("Log files written to ${resultJsonUri.toFilePath()} and"
" ${logsJsonUri.toFilePath()}");
// The testing framework (pkg/testing) sets the exitCode to 1 if any test
// failed, so we reset it here to indicate that the test runner was
// successful.
exitCode = 0;
// Return with exit code 1 if at least one suite timed out.
bool timeout = timeouts.any((timeout) => timeout);
if (timeout) {
exitCode = 1;
} else {
// The testing framework (pkg/testing) sets the exitCode to 1 if any test
// failed, so we reset it here to indicate that the test runner was
// successful.
exitCode = 0;
}
}
+2 -1
View File
@@ -136,6 +136,7 @@ abstract class ChainContext {
Map<TestDescription, Set<Expectation>> unexpectedOutcomes =
<TestDescription, Set<Expectation>>{};
int completed = 0;
logger.logSuiteStarted(suite);
List<Future> futures = <Future>[];
for (TestDescription description in descriptions) {
String selector = "${suite.name}/${description.shortName}";
@@ -236,7 +237,7 @@ abstract class ChainContext {
await doStep(description);
}
await Future.wait(futures);
logger.logSuiteComplete();
logger.logSuiteComplete(suite);
if (unexpectedResults.isNotEmpty) {
unexpectedResults.forEach((TestDescription description, Result result) {
logger.logUnexpectedResult(
+8 -2
View File
@@ -63,7 +63,9 @@ abstract class Logger {
void logUnexpectedResult(Suite suite, TestDescription description,
Result result, Set<Expectation> expectedOutcomes);
void logSuiteComplete();
void logSuiteStarted(Suite suite);
void logSuiteComplete(Suite suite);
void logUncaughtError(error, StackTrace stackTrace);
}
@@ -167,7 +169,11 @@ class StdoutLogger implements Logger {
}
}
void logSuiteComplete() {
void logSuiteStarted(Suite suite) {
print("Running suite ${suite.name}...");
}
void logSuiteComplete(Suite suite) {
if (!isVerbose) {
print("");
}
-1
View File
@@ -62,7 +62,6 @@ Future<Null> runMe(List<String> arguments, CreateContext f,
if (cl.verbose) enableVerboseOutput();
for (Chain suite in testRoot.toolChains) {
if (me == suite.source) {
print("Running suite ${suite.name}...");
ChainContext context = await f(suite, cl.environment);
await context.run(suite, new Set<String>.from(cl.selectors),
shards: shards, shard: shard, logger: logger);
-1
View File
@@ -188,5 +188,4 @@ Future<void> runTests(Map<String, Function> tests) =>
const StdoutLogger()
.logTestComplete(++completed, 0, tests.length, null, null);
}
const StdoutLogger().logSuiteComplete();
});