Allow injecting an IOSink into the reporters (#1101)
Towards #1100
Prepare for a world where we a reporter can be configured to write directly to a file.
diff --git a/pkgs/test_core/CHANGELOG.md b/pkgs/test_core/CHANGELOG.md
index ee9ccaa..93ea471 100644
--- a/pkgs/test_core/CHANGELOG.md
+++ b/pkgs/test_core/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.2.15-dev
+
+* Add an `IOSink` argument to reporters to prepare for reporting to a file.
+
## 0.2.14
* Support the latest `package:analyzer`.
diff --git a/pkgs/test_core/lib/src/runner.dart b/pkgs/test_core/lib/src/runner.dart
index 5712035..68a3331 100644
--- a/pkgs/test_core/lib/src/runner.dart
+++ b/pkgs/test_core/lib/src/runner.dart
@@ -75,7 +75,8 @@
Engine(concurrency: config.concurrency, coverage: config.coverage);
var reporterDetails = allReporters[config.reporter];
- return Runner._(engine, reporterDetails.factory(config, engine));
+ return Runner._(
+ engine, reporterDetails.factory(config, engine, stdout));
});
Runner._(this._engine, this._reporter);
diff --git a/pkgs/test_core/lib/src/runner/configuration/reporters.dart b/pkgs/test_core/lib/src/runner/configuration/reporters.dart
index 1bf5663..d9ff380 100644
--- a/pkgs/test_core/lib/src/runner/configuration/reporters.dart
+++ b/pkgs/test_core/lib/src/runner/configuration/reporters.dart
@@ -15,8 +15,7 @@
/// Constructs a reporter for the provided engine with the provided
/// configuration.
-typedef ReporterFactory = Reporter Function(
- Configuration configuration, Engine engine);
+typedef ReporterFactory = Reporter Function(Configuration, Engine, IOSink);
/// Container for a reporter description and corresponding factory.
class ReporterDetails {
@@ -32,16 +31,17 @@
final _allReporters = <String, ReporterDetails>{
"expanded": ReporterDetails(
"A separate line for each update.",
- (config, engine) => ExpandedReporter.watch(engine,
+ (config, engine, sink) => ExpandedReporter.watch(engine,
color: config.color,
printPath: config.paths.length > 1 ||
Directory(config.paths.single).existsSync(),
- printPlatform: config.suiteDefaults.runtimes.length > 1)),
+ printPlatform: config.suiteDefaults.runtimes.length > 1,
+ sink: sink)),
"compact": ReporterDetails("A single line, updated continuously.",
- (_, engine) => CompactReporter.watch(engine)),
+ (_, engine, sink) => CompactReporter.watch(engine, sink)),
"json": ReporterDetails(
"A machine-readable format (see https://goo.gl/gBsV1a).",
- (_, engine) => JsonReporter.watch(engine)),
+ (_, engine, sink) => JsonReporter.watch(engine, sink)),
};
final defaultReporter =
diff --git a/pkgs/test_core/lib/src/runner/reporter/compact.dart b/pkgs/test_core/lib/src/runner/reporter/compact.dart
index a0a6507..d8166ae 100644
--- a/pkgs/test_core/lib/src/runner/reporter/compact.dart
+++ b/pkgs/test_core/lib/src/runner/reporter/compact.dart
@@ -56,6 +56,8 @@
/// The engine used to run the tests.
final Engine _engine;
+ final IOSink _sink;
+
/// A stopwatch that tracks the duration of the full run.
final _stopwatch = Stopwatch();
@@ -101,9 +103,10 @@
/// Watches the tests run by [engine] and prints their results to the
/// terminal.
- static CompactReporter watch(Engine engine) => CompactReporter._(engine);
+ static CompactReporter watch(Engine engine, IOSink sink) =>
+ CompactReporter._(engine, sink);
- CompactReporter._(this._engine) {
+ CompactReporter._(this._engine, this._sink) {
_subscriptions.add(_engine.onTestStarted.listen(_onTestStarted));
/// Convert the future to a stream so that the subscription can be paused or
@@ -115,7 +118,7 @@
if (_paused) return;
_paused = true;
- if (!_printedNewline) print('');
+ if (!_printedNewline) _sink.writeln('');
_printedNewline = true;
_stopwatch.stop();
@@ -173,12 +176,12 @@
_subscriptions.add(liveTest.onMessage.listen((message) {
_progressLine(_description(liveTest), truncate: false);
- if (!_printedNewline) print('');
+ if (!_printedNewline) _sink.writeln('');
_printedNewline = true;
var text = message.text;
if (message.type == MessageType.skip) text = ' $_yellow$text$_noColor';
- print(text);
+ _sink.writeln(text);
}));
}
@@ -205,24 +208,24 @@
_progressLine(_description(liveTest),
truncate: false, suffix: " $_bold$_red[E]$_noColor");
- if (!_printedNewline) print('');
+ if (!_printedNewline) _sink.writeln('');
_printedNewline = true;
if (error is! LoadException) {
- print(indent(error.toString()));
- print(indent('$stackTrace'));
+ _sink.writeln(indent(error.toString()));
+ _sink.writeln(indent('$stackTrace'));
return;
}
// TODO - what type is this?
- print(indent(error.toString(color: _config.color) as String));
+ _sink.writeln(indent(error.toString(color: _config.color) as String));
// Only print stack traces for load errors that come from the user's code.
if (error.innerError is! IOException &&
error.innerError is! IsolateSpawnException &&
error.innerError is! FormatException &&
error.innerError is! String) {
- print(indent('$stackTrace'));
+ _sink.writeln(indent('$stackTrace'));
}
}
@@ -239,34 +242,34 @@
// shouldn't print summary information, we should just make sure the
// terminal cursor is on its own line.
if (success == null) {
- if (!_printedNewline) print("");
+ if (!_printedNewline) _sink.writeln("");
_printedNewline = true;
return;
}
if (_engine.liveTests.isEmpty) {
- if (!_printedNewline) stdout.write("\r");
+ if (!_printedNewline) _sink.write("\r");
var message = "No tests ran.";
- stdout.write(message);
+ _sink.write(message);
// Add extra padding to overwrite any load messages.
- if (!_printedNewline) stdout.write(" " * (lineLength - message.length));
- stdout.writeln();
+ if (!_printedNewline) _sink.write(" " * (lineLength - message.length));
+ _sink.writeln('');
} else if (!success) {
for (var liveTest in _engine.active) {
_progressLine(_description(liveTest),
truncate: false,
suffix: " - did not complete $_bold$_red[E]$_noColor");
- print('');
+ _sink.writeln('');
}
_progressLine('Some tests failed.', color: _red);
- print('');
+ _sink.writeln('');
} else if (_engine.passed.isEmpty) {
_progressLine("All tests skipped.");
- print('');
+ _sink.writeln('');
} else {
_progressLine("All tests passed!");
- print('');
+ _sink.writeln('');
}
}
@@ -344,7 +347,7 @@
// Pad the rest of the line so that it looks erased.
buffer.write(' ' * (lineLength - withoutColors(buffer.toString()).length));
- stdout.write(buffer.toString());
+ _sink.write(buffer.toString());
_printedNewline = false;
return true;
diff --git a/pkgs/test_core/lib/src/runner/reporter/expanded.dart b/pkgs/test_core/lib/src/runner/reporter/expanded.dart
index d2b3454..9e87345 100644
--- a/pkgs/test_core/lib/src/runner/reporter/expanded.dart
+++ b/pkgs/test_core/lib/src/runner/reporter/expanded.dart
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
+import 'dart:io';
import 'package:test_api/src/backend/live_test.dart'; // ignore: implementation_imports
import 'package:test_api/src/backend/message.dart'; // ignore: implementation_imports
@@ -84,6 +85,8 @@
/// The set of all subscriptions to various streams.
final _subscriptions = Set<StreamSubscription>();
+ final IOSink _sink;
+
// TODO(nweiz): Get configuration from [Configuration.current] once we have
// cross-platform imports.
/// Watches the tests run by [engine] and prints their results to the
@@ -94,15 +97,25 @@
/// the test description. Likewise, if [printPlatform] is `true`, this will
/// print the platform as part of the test description.
static ExpandedReporter watch(Engine engine,
- {bool color = true, bool printPath = true, bool printPlatform = true}) {
+ {bool color = true,
+ bool printPath = true,
+ bool printPlatform = true,
+ IOSink sink}) {
return ExpandedReporter._(engine,
- color: color, printPath: printPath, printPlatform: printPlatform);
+ color: color,
+ printPath: printPath,
+ printPlatform: printPlatform,
+ sink: sink ?? stdout);
}
ExpandedReporter._(this._engine,
- {bool color = true, bool printPath = true, bool printPlatform = true})
+ {bool color = true,
+ bool printPath = true,
+ bool printPlatform = true,
+ IOSink sink})
: _printPath = printPath,
_printPlatform = printPlatform,
+ _sink = sink,
_color = color,
_green = color ? '\u001b[32m' : '',
_red = color ? '\u001b[31m' : '',
@@ -173,7 +186,7 @@
_progressLine(_description(liveTest));
var text = message.text;
if (message.type == MessageType.skip) text = ' $_yellow$text$_noColor';
- print(text);
+ _sink.writeln(text);
}));
}
@@ -195,17 +208,16 @@
_progressLine(_description(liveTest), suffix: " $_bold$_red[E]$_noColor");
if (error is! LoadException) {
- print(indent(error.toString()));
- print(indent('$stackTrace'));
+ _sink..writeln(indent('$error'))..writeln(indent('$stackTrace'));
return;
}
// TODO - what type is this?
- print(indent((error as dynamic).toString(color: _color) as String));
+ _sink.writeln(indent((error as dynamic).toString(color: _color) as String));
// Only print stack traces for load errors that come from the user's code.
if (error.innerError is! FormatException && error.innerError is! String) {
- print(indent('$stackTrace'));
+ _sink.writeln(indent('$stackTrace'));
}
}
@@ -220,7 +232,7 @@
if (success == null) return;
if (_engine.liveTests.isEmpty) {
- print("No tests ran.");
+ _sink.writeln("No tests ran.");
} else if (!success) {
for (var liveTest in _engine.active) {
_progressLine(_description(liveTest),
@@ -287,7 +299,7 @@
buffer.write(message);
buffer.write(_noColor);
- print(buffer.toString());
+ _sink.writeln(buffer.toString());
}
/// Returns a representation of [duration] as `MM:SS`.
diff --git a/pkgs/test_core/lib/src/runner/reporter/json.dart b/pkgs/test_core/lib/src/runner/reporter/json.dart
index 1509099..2a2bd12 100644
--- a/pkgs/test_core/lib/src/runner/reporter/json.dart
+++ b/pkgs/test_core/lib/src/runner/reporter/json.dart
@@ -4,7 +4,7 @@
import 'dart:async';
import 'dart:convert';
-import 'dart:io' show pid;
+import 'dart:io' show IOSink, pid;
import 'package:path/path.dart' as p;
@@ -33,6 +33,8 @@
/// The engine used to run the tests.
final Engine _engine;
+ final IOSink _sink;
+
/// A stopwatch that tracks the duration of the full run.
final _stopwatch = Stopwatch();
@@ -61,9 +63,10 @@
var _nextID = 0;
/// Watches the tests run by [engine] and prints their results as JSON.
- static JsonReporter watch(Engine engine) => JsonReporter._(engine);
+ static JsonReporter watch(Engine engine, IOSink sink) =>
+ JsonReporter._(engine, sink);
- JsonReporter._(this._engine) : _config = Configuration.current {
+ JsonReporter._(this._engine, this._sink) : _config = Configuration.current {
_subscriptions.add(_engine.onTestStarted.listen(_onTestStarted));
/// Convert the future to a stream so that the subscription can be paused or
@@ -283,7 +286,7 @@
void _emit(String type, Map attributes) {
attributes["type"] = type;
attributes["time"] = _stopwatch.elapsed.inMilliseconds;
- print(jsonEncode(attributes));
+ _sink.writeln(jsonEncode(attributes));
}
/// Modifies [map] to include line, column, and URL information from the first
diff --git a/pkgs/test_core/pubspec.yaml b/pkgs/test_core/pubspec.yaml
index 5eb1cc4..54b904c 100644
--- a/pkgs/test_core/pubspec.yaml
+++ b/pkgs/test_core/pubspec.yaml
@@ -1,5 +1,5 @@
name: test_core
-version: 0.2.14
+version: 0.2.15-dev
author: Dart Team <misc@dartlang.org>
description: A basic library for writing tests and running them on the VM.
homepage: https://github.com/dart-lang/test/blob/master/pkgs/test_core