[pkg/testing] Replace top-level logging functions with an interface.

Change-Id: Id896436ab869d52c6e9befff885494b87690a342
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/118382
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Karl Klose <karlklose@google.com>
This commit is contained in:
Karl Klose 2019-09-23 07:57:39 +00:00 committed by commit-bot@chromium.org
parent 209180d280
commit 96f1abdaca
5 changed files with 157 additions and 123 deletions

View file

@ -21,15 +21,7 @@ import 'zone_helper.dart' show runGuarded;
import 'error_handling.dart' show withErrorHandling;
import 'log.dart'
show
logMessage,
logStepComplete,
logStepStart,
logSuiteComplete,
logTestComplete,
logUnexpectedResult,
splitLines;
import 'log.dart' show Logger, StdoutLogger, splitLines;
import 'multitest.dart' show MultitestTransformer, isError;
@ -112,7 +104,9 @@ abstract class ChainContext {
ExpectationSet get expectationSet => ExpectationSet.Default;
Future<Null> run(Chain suite, Set<String> selectors,
{int shards = 1, int shard = 0}) async {
{int shards = 1,
int shard = 0,
Logger logger: const StdoutLogger()}) async {
assert(shards >= 1, "Invalid shards count: $shards");
assert(0 <= shard && shard < shards,
"Invalid shard index: $shard, not in range [0,$shards[.");
@ -182,8 +176,8 @@ abstract class ChainContext {
Step step = iterator.current;
lastStepRun = step;
isAsync = step.isAsync;
logStepStart(completed, unexpectedResults.length, descriptions.length,
suite, description, step);
logger.logStepStart(completed, unexpectedResults.length,
descriptions.length, suite, description, step);
// TODO(ahe): It's important to share the zone error reporting zone
// between all the tasks. Otherwise, if a future completes with an
// error in one zone, and gets stored, it becomes an uncaught error
@ -201,7 +195,7 @@ abstract class ChainContext {
future = future.then((_currentResult) async {
Result currentResult = _currentResult;
if (currentResult != null) {
logStepComplete(completed, unexpectedResults.length,
logger.logStepComplete(completed, unexpectedResults.length,
descriptions.length, suite, description, lastStepRun);
result = currentResult;
if (currentResult.outcome == Expectation.Pass) {
@ -217,12 +211,13 @@ abstract class ChainContext {
result.addLog("$sb");
unexpectedResults[description] = result;
unexpectedOutcomes[description] = expectedOutcomes;
logUnexpectedResult(suite, description, result, expectedOutcomes);
logger.logUnexpectedResult(
suite, description, result, expectedOutcomes);
exitCode = 1;
} else {
logMessage(sb);
logger.logMessage(sb);
}
logTestComplete(++completed, unexpectedResults.length,
logger.logTestComplete(++completed, unexpectedResults.length,
descriptions.length, suite, description);
});
if (isAsync) {
@ -233,14 +228,16 @@ abstract class ChainContext {
}
}
logger.logTestStart(completed, unexpectedResults.length,
descriptions.length, suite, description);
// The input of the first step is [description].
await doStep(description);
}
await Future.wait(futures);
logSuiteComplete();
logger.logSuiteComplete();
if (unexpectedResults.isNotEmpty) {
unexpectedResults.forEach((TestDescription description, Result result) {
logUnexpectedResult(
logger.logUnexpectedResult(
suite, description, result, unexpectedOutcomes[description]);
});
print("${unexpectedResults.length} failed:");

View file

@ -38,106 +38,146 @@ void enableVerboseOutput() {
_isVerbose = true;
}
void logTestComplete(int completed, int failed, int total, Suite suite,
TestDescription description) {
String message = formatProgress(completed, failed, total);
if (suite != null) {
message += ": ${formatTestDescription(suite, description)}";
}
logProgress(message);
abstract class Logger {
void logTestStart(int completed, int failed, int total, Suite suite,
TestDescription description);
void logTestComplete(int completed, int failed, int total, Suite suite,
TestDescription description);
void logStepStart(int completed, int failed, int total, Suite suite,
TestDescription description, Step step);
void logStepComplete(int completed, int failed, int total, Suite suite,
TestDescription description, Step step);
void logProgress(String message);
void logMessage(Object message);
void logNumberedLines(String text);
void logExpectedResult(Suite suite, TestDescription description,
Result result, Set<Expectation> expectedOutcomes);
void logUnexpectedResult(Suite suite, TestDescription description,
Result result, Set<Expectation> expectedOutcomes);
void logSuiteComplete();
void logUncaughtError(error, StackTrace stackTrace);
}
void logStepStart(int completed, int failed, int total, Suite suite,
TestDescription description, Step step) {
String message = formatProgress(completed, failed, total);
if (suite != null) {
message += ": ${formatTestDescription(suite, description)} ${step.name}";
if (step.isAsync) {
message += "...";
class StdoutLogger implements Logger {
const StdoutLogger();
void logTestStart(int completed, int failed, int total, Suite suite,
TestDescription description) {}
void logTestComplete(int completed, int failed, int total, Suite suite,
TestDescription description) {
String message = formatProgress(completed, failed, total);
if (suite != null) {
message += ": ${formatTestDescription(suite, description)}";
}
logProgress(message);
}
logProgress(message);
}
void logStepComplete(int completed, int failed, int total, Suite suite,
TestDescription description, Step step) {
if (!step.isAsync) return;
String message = formatProgress(completed, failed, total);
if (suite != null) {
message += ": ${formatTestDescription(suite, description)} ${step.name}!";
void logStepStart(int completed, int failed, int total, Suite suite,
TestDescription description, Step step) {
String message = formatProgress(completed, failed, total);
if (suite != null) {
message += ": ${formatTestDescription(suite, description)} ${step.name}";
if (step.isAsync) {
message += "...";
}
}
logProgress(message);
}
logProgress(message);
}
void logProgress(String message) {
if (isVerbose) {
print(message);
} else {
print("$eraseLine$message$cursorUp");
void logStepComplete(int completed, int failed, int total, Suite suite,
TestDescription description, Step step) {
if (!step.isAsync) return;
String message = formatProgress(completed, failed, total);
if (suite != null) {
message += ": ${formatTestDescription(suite, description)} ${step.name}!";
}
logProgress(message);
}
}
String formatProgress(int completed, int failed, int total) {
Duration elapsed = wallclock.elapsed;
String percent = pad((completed / total * 100.0).toStringAsFixed(1), 5);
String good = pad(completed - failed, 5);
String bad = pad(failed, 5);
String minutes = pad(elapsed.inMinutes, 2, filler: "0");
String seconds = pad(elapsed.inSeconds % 60, 2, filler: "0");
return "[ $minutes:$seconds | $percent% | +$good | -$bad ]";
}
String formatTestDescription(Suite suite, TestDescription description) {
return "${suite.name}/${description.shortName}";
}
void logMessage(Object message) {
if (isVerbose) {
print("$message");
}
}
void logNumberedLines(String text) {
if (isVerbose) {
print(numberedLines(text));
}
}
void logUnexpectedResult(Suite suite, TestDescription description,
Result result, Set<Expectation> expectedOutcomes) {
print("${eraseLine}UNEXPECTED: ${suite.name}/${description.shortName}");
Uri statusFile = suite.statusFile;
if (statusFile != null) {
String path = statusFile.toFilePath();
if (result.outcome == Expectation.Pass) {
print("The test unexpectedly passed, please update $path.");
void logProgress(String message) {
if (isVerbose) {
print(message);
} else {
print("The test had the outcome ${result.outcome}, but the status file "
"($path) allows these outcomes: ${expectedOutcomes.join(' ')}");
print("$eraseLine$message$cursorUp");
}
}
String log = result.log;
if (log.isNotEmpty) {
print(log);
String formatProgress(int completed, int failed, int total) {
Duration elapsed = wallclock.elapsed;
String percent = pad((completed / total * 100.0).toStringAsFixed(1), 5);
String good = pad(completed - failed, 5);
String bad = pad(failed, 5);
String minutes = pad(elapsed.inMinutes, 2, filler: "0");
String seconds = pad(elapsed.inSeconds % 60, 2, filler: "0");
return "[ $minutes:$seconds | $percent% | +$good | -$bad ]";
}
if (result.error != null) {
print(result.error);
if (result.trace != null) {
print(result.trace);
String formatTestDescription(Suite suite, TestDescription description) {
return "${suite.name}/${description.shortName}";
}
void logMessage(Object message) {
if (isVerbose) {
print("$message");
}
}
}
void logSuiteComplete() {
if (!isVerbose) {
print("");
void logNumberedLines(String text) {
if (isVerbose) {
print(numberedLines(text));
}
}
}
void logUncaughtError(error, StackTrace stackTrace) {
logMessage(error);
if (stackTrace != null) {
logMessage(stackTrace);
void logExpectedResult(Suite suite, TestDescription description,
Result result, Set<Expectation> expectedOutcomes) {}
void logUnexpectedResult(Suite suite, TestDescription description,
Result result, Set<Expectation> expectedOutcomes) {
print("${eraseLine}UNEXPECTED: ${suite.name}/${description.shortName}");
Uri statusFile = suite.statusFile;
if (statusFile != null) {
String path = statusFile.toFilePath();
if (result.outcome == Expectation.Pass) {
print("The test unexpectedly passed, please update $path.");
} else {
print("The test had the outcome ${result.outcome}, but the status file "
"($path) allows these outcomes: ${expectedOutcomes.join(' ')}");
}
}
String log = result.log;
if (log.isNotEmpty) {
print(log);
}
if (result.error != null) {
print(result.error);
if (result.trace != null) {
print(result.trace);
}
}
}
void logSuiteComplete() {
if (!isVerbose) {
print("");
}
}
void logUncaughtError(error, StackTrace stackTrace) {
logMessage(error);
if (stackTrace != null) {
logMessage(stackTrace);
}
}
}

View file

@ -24,12 +24,7 @@ import '../testing.dart'
import 'analyze.dart' show Analyze;
import 'log.dart'
show
enableVerboseOutput,
isVerbose,
logMessage,
logNumberedLines,
splitLines;
show enableVerboseOutput, isVerbose, Logger, splitLines, StdoutLogger;
import 'suite.dart' show Dart, Suite;
@ -55,7 +50,11 @@ Future<TestRoot> computeTestRoot(String configurationPath, Uri base) {
/// `testing.json` isn't located in the current working directory and is a path
/// relative to [me] which defaults to `Platform.script`.
Future<Null> runMe(List<String> arguments, CreateContext f,
{String configurationPath, Uri me, int shards = 1, int shard = 0}) {
{String configurationPath,
Uri me,
int shards = 1,
int shard = 0,
Logger logger: const StdoutLogger()}) {
me ??= Platform.script;
return withErrorHandling(() async {
TestRoot testRoot = await computeTestRoot(configurationPath, me);
@ -66,7 +65,7 @@ Future<Null> runMe(List<String> arguments, CreateContext f,
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);
shards: shards, shard: shard, logger: logger);
}
}
});
@ -117,8 +116,8 @@ Future<Null> run(List<String> arguments, List<String> suiteNames,
}
Future<Null> runProgram(String program, Uri packages) async {
logMessage("Running:");
logNumberedLines(program);
const StdoutLogger().logMessage("Running:");
const StdoutLogger().logNumberedLines(program);
Uri dataUri = new Uri.dataFromString(program);
ReceivePort exitPort = new ReceivePort();
Isolate isolate = await Isolate.spawnUri(dataUri, <String>[], null,

View file

@ -18,13 +18,7 @@ import 'test_root.dart' show TestRoot;
import 'zone_helper.dart' show runGuarded;
import 'log.dart'
show
enableVerboseOutput,
isVerbose,
logMessage,
logSuiteComplete,
logTestComplete;
import 'log.dart' show enableVerboseOutput, isVerbose, StdoutLogger;
import 'run.dart' show SuiteRunner, runProgram;
@ -115,7 +109,8 @@ class CommandLine {
}
}
}
logMessage("Reading configuration file '$configurationPath'.");
const StdoutLogger()
.logMessage("Reading configuration file '$configurationPath'.");
Uri configuration =
await Isolate.resolvePackageUri(Uri.base.resolve(configurationPath));
if (configuration == null ||
@ -177,18 +172,21 @@ Future<void> runTests(Map<String, Function> tests) =>
withErrorHandling<void>(() async {
int completed = 0;
for (String name in tests.keys) {
const StdoutLogger()
.logTestStart(completed, 0, tests.length, null, null);
StringBuffer sb = new StringBuffer();
try {
await runGuarded(() {
print("Running test $name");
return tests[name]();
}, printLineOnStdout: sb.writeln);
logMessage(sb);
const StdoutLogger().logMessage(sb);
} catch (e) {
print(sb);
rethrow;
}
logTestComplete(++completed, 0, tests.length, null, null);
const StdoutLogger()
.logTestComplete(++completed, 0, tests.length, null, null);
}
logSuiteComplete();
const StdoutLogger().logSuiteComplete();
});

View file

@ -11,7 +11,7 @@ import 'dart:io' show exit, stderr;
import 'dart:isolate' show Capability, Isolate, ReceivePort;
import 'log.dart' show logUncaughtError;
import 'log.dart' show StdoutLogger;
Future runGuarded(Future f(),
{void printLineOnStdout(line),
@ -26,7 +26,7 @@ Future runGuarded(Future f(),
Completer completer = new Completer();
handleUncaughtError(error, StackTrace stackTrace) {
logUncaughtError(error, stackTrace);
StdoutLogger().logUncaughtError(error, stackTrace);
if (!completer.isCompleted) {
completer.completeError(error, stackTrace);
} else if (handleLateError != null) {