mirror of
https://github.com/dart-lang/sdk
synced 2024-07-20 18:05:01 +00:00
[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:
parent
209180d280
commit
96f1abdaca
|
@ -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:");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in a new issue