| Index: lib/src/runner/reporter/json.dart
|
| diff --git a/lib/src/runner/reporter/json.dart b/lib/src/runner/reporter/json.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..b0c45bbcf53125927146504e61bae27ffe1f4eeb
|
| --- /dev/null
|
| +++ b/lib/src/runner/reporter/json.dart
|
| @@ -0,0 +1,168 @@
|
| +// Copyright (c) 2015, 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 file.
|
| +
|
| +library test.runner.reporter.json;
|
| +
|
| +import 'dart:async';
|
| +import 'dart:convert';
|
| +
|
| +import '../../backend/live_test.dart';
|
| +import '../../utils.dart';
|
| +import '../engine.dart';
|
| +import '../load_exception.dart';
|
| +import '../reporter.dart';
|
| +import '../version.dart';
|
| +
|
| +/// A reporter that prints machine-readable JSON-formatted test results.
|
| +class JsonReporter implements Reporter {
|
| + /// Whether to use verbose stack traces.
|
| + final bool _verboseTrace;
|
| +
|
| + /// The engine used to run the tests.
|
| + final Engine _engine;
|
| +
|
| + /// A stopwatch that tracks the duration of the full run.
|
| + final _stopwatch = new Stopwatch();
|
| +
|
| + /// Whether we've started [_stopwatch].
|
| + ///
|
| + /// We can't just use `_stopwatch.isRunning` because the stopwatch is stopped
|
| + /// when the reporter is paused.
|
| + var _stopwatchStarted = false;
|
| +
|
| + /// Whether the reporter is paused.
|
| + var _paused = false;
|
| +
|
| + /// The set of all subscriptions to various streams.
|
| + final _subscriptions = new Set<StreamSubscription>();
|
| +
|
| + /// An expando that associates unique IDs with [LiveTest]s.
|
| + final _ids = new Map<LiveTest, int>();
|
| +
|
| + /// The next ID to associate with a [LiveTest].
|
| + var _nextID = 0;
|
| +
|
| + /// Watches the tests run by [engine] and prints their results as JSON.
|
| + ///
|
| + /// If [verboseTrace] is `true`, this will print core library frames.
|
| + static JsonReporter watch(Engine engine, {bool verboseTrace: false}) {
|
| + return new JsonReporter._(engine, verboseTrace: verboseTrace);
|
| + }
|
| +
|
| + JsonReporter._(this._engine, {bool verboseTrace: false})
|
| + : _verboseTrace = verboseTrace {
|
| + _subscriptions.add(_engine.onTestStarted.listen(_onTestStarted));
|
| +
|
| + /// Convert the future to a stream so that the subscription can be paused or
|
| + /// canceled.
|
| + _subscriptions.add(_engine.success.asStream().listen(_onDone));
|
| +
|
| + _emit("start", {
|
| + "protocolVersion": "0.1.0",
|
| + "runnerVersion": testVersion
|
| + });
|
| + }
|
| +
|
| + void pause() {
|
| + if (_paused) return;
|
| + _paused = true;
|
| +
|
| + _stopwatch.stop();
|
| +
|
| + for (var subscription in _subscriptions) {
|
| + subscription.pause();
|
| + }
|
| + }
|
| +
|
| + void resume() {
|
| + if (!_paused) return;
|
| + _paused = false;
|
| +
|
| + if (_stopwatchStarted) _stopwatch.start();
|
| +
|
| + for (var subscription in _subscriptions) {
|
| + subscription.resume();
|
| + }
|
| + }
|
| +
|
| + void cancel() {
|
| + for (var subscription in _subscriptions) {
|
| + subscription.cancel();
|
| + }
|
| + _subscriptions.clear();
|
| + }
|
| +
|
| + /// A callback called when the engine begins running [liveTest].
|
| + void _onTestStarted(LiveTest liveTest) {
|
| + if (!_stopwatchStarted) {
|
| + _stopwatchStarted = true;
|
| + _stopwatch.start();
|
| + }
|
| +
|
| + var id = _nextID++;
|
| + _ids[liveTest] = id;
|
| + _emit("testStart", {
|
| + "test": {
|
| + "id": id,
|
| + "name": liveTest.test.name,
|
| + "metadata": {
|
| + "skip": liveTest.test.metadata.skip,
|
| + "skipReason": liveTest.test.metadata.skipReason
|
| + }
|
| + }
|
| + });
|
| +
|
| + /// Convert the future to a stream so that the subscription can be paused or
|
| + /// canceled.
|
| + _subscriptions.add(liveTest.onComplete.asStream().listen((_) =>
|
| + _onComplete(liveTest)));
|
| +
|
| + _subscriptions.add(liveTest.onError.listen((error) =>
|
| + _onError(liveTest, error.error, error.stackTrace)));
|
| +
|
| + _subscriptions.add(liveTest.onPrint.listen((line) {
|
| + _emit("print", {
|
| + "testID": id,
|
| + "message": line
|
| + });
|
| + }));
|
| + }
|
| +
|
| + /// A callback called when [liveTest] finishes running.
|
| + void _onComplete(LiveTest liveTest) {
|
| + _emit("testDone", {
|
| + "testID": _ids[liveTest],
|
| + "result": liveTest.state.result.toString(),
|
| + "hidden": !_engine.liveTests.contains(liveTest)
|
| + });
|
| + }
|
| +
|
| + /// A callback called when [liveTest] throws [error].
|
| + void _onError(LiveTest liveTest, error, StackTrace stackTrace) {
|
| + _emit("error", {
|
| + "testID": _ids[liveTest],
|
| + "error": error.toString(),
|
| + "stackTrace": terseChain(stackTrace, verbose: _verboseTrace).toString(),
|
| + "isFailure": error is LoadException
|
| + });
|
| + }
|
| +
|
| + /// A callback called when the engine is finished running tests.
|
| + ///
|
| + /// [success] will be `true` if all tests passed, `false` if some tests
|
| + /// failed, and `null` if the engine was closed prematurely.
|
| + void _onDone(bool success) {
|
| + cancel();
|
| + _stopwatch.stop();
|
| +
|
| + _emit("done", {"success": success});
|
| + }
|
| +
|
| + /// Emits an event with the given type and attributes.
|
| + void _emit(String type, Map attributes) {
|
| + attributes["type"] = type;
|
| + attributes["time"] = _stopwatch.elapsed.inMilliseconds;
|
| + print(JSON.encode(attributes));
|
| + }
|
| +}
|
|
|