blob: edd366384db274a6f0a5363d5d45d32eca4f916b [file] [log] [blame]
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE.md file.
library testing.log;
import 'dart:io' show stdout;
import 'chain.dart' show Result, Step;
import 'suite.dart' show Suite;
import 'test_description.dart' show TestDescription;
import 'expectation.dart' show Expectation;
/// ANSI escape code for moving cursor one line up.
/// See [CSI codes](https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_codes).
const String cursorUpCodes = "\u001b[1A";
/// ANSI escape code for erasing the entire line.
/// See [CSI codes](https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_codes).
const String eraseLineCodes = "\u001b[2K";
final bool enableAnsiEscapes = stdout.supportsAnsiEscapes;
final String cursorUp = enableAnsiEscapes ? cursorUpCodes : "";
final String eraseLine = enableAnsiEscapes ? eraseLineCodes : "";
final Stopwatch wallclock = new Stopwatch()..start();
bool _isVerbose = const bool.fromEnvironment("verbose");
bool get isVerbose => _isVerbose;
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);
}
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);
}
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);
}
void logProgress(String message) {
if (isVerbose) {
print(message);
} else {
print("$eraseLine$message$cursorUp");
}
}
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.");
} 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);
}
}
String pad(Object o, int pad, {String filler: " "}) {
String result = (filler * pad) + "$o";
return result.substring(result.length - pad);
}
String numberedLines(String text) {
StringBuffer result = new StringBuffer();
int lineNumber = 1;
List<String> lines = splitLines(text);
int pad = "${lines.length}".length;
String fill = " " * pad;
for (String line in lines) {
String paddedLineNumber = "$fill$lineNumber";
paddedLineNumber =
paddedLineNumber.substring(paddedLineNumber.length - pad);
result.write("$paddedLineNumber: $line");
lineNumber++;
}
return '$result';
}
List<String> splitLines(String text) {
return text.split(new RegExp('^', multiLine: true));
}