| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library test.runner.reporter.compact; | 5 library test.runner.reporter.compact; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:io'; | 8 import 'dart:io'; |
| 9 import 'dart:isolate'; | 9 import 'dart:isolate'; |
| 10 | 10 |
| 11 import '../../backend/live_test.dart'; | 11 import '../../backend/live_test.dart'; |
| 12 import '../../backend/state.dart'; | 12 import '../../backend/state.dart'; |
| 13 import '../../utils.dart'; | 13 import '../../utils.dart'; |
| 14 import '../../utils.dart' as utils; | 14 import '../../utils.dart' as utils; |
| 15 import '../engine.dart'; | 15 import '../engine.dart'; |
| 16 import '../load_exception.dart'; | 16 import '../load_exception.dart'; |
| 17 import '../load_suite.dart'; | 17 import '../load_suite.dart'; |
| 18 import '../reporter.dart'; |
| 18 | 19 |
| 19 /// The maximum console line length. | 20 /// The maximum console line length. |
| 20 /// | 21 /// |
| 21 /// Lines longer than this will be cropped. | 22 /// Lines longer than this will be cropped. |
| 22 const _lineLength = 100; | 23 const _lineLength = 100; |
| 23 | 24 |
| 24 /// A reporter that prints test results to the console in a single | 25 /// A reporter that prints test results to the console in a single |
| 25 /// continuously-updating line. | 26 /// continuously-updating line. |
| 26 class CompactReporter { | 27 class CompactReporter implements Reporter { |
| 27 /// Whether the reporter should emit terminal color escapes. | 28 /// Whether the reporter should emit terminal color escapes. |
| 28 final bool _color; | 29 final bool _color; |
| 29 | 30 |
| 30 /// The terminal escape for green text, or the empty string if this is Windows | 31 /// The terminal escape for green text, or the empty string if this is Windows |
| 31 /// or not outputting to a terminal. | 32 /// or not outputting to a terminal. |
| 32 final String _green; | 33 final String _green; |
| 33 | 34 |
| 34 /// The terminal escape for red text, or the empty string if this is Windows | 35 /// The terminal escape for red text, or the empty string if this is Windows |
| 35 /// or not outputting to a terminal. | 36 /// or not outputting to a terminal. |
| 36 final String _red; | 37 final String _red; |
| (...skipping 22 matching lines...) Expand all Loading... |
| 59 | 60 |
| 60 /// Whether the path to each test's suite should be printed. | 61 /// Whether the path to each test's suite should be printed. |
| 61 final bool _printPath; | 62 final bool _printPath; |
| 62 | 63 |
| 63 /// Whether the platform each test is running on should be printed. | 64 /// Whether the platform each test is running on should be printed. |
| 64 final bool _printPlatform; | 65 final bool _printPlatform; |
| 65 | 66 |
| 66 /// A stopwatch that tracks the duration of the full run. | 67 /// A stopwatch that tracks the duration of the full run. |
| 67 final _stopwatch = new Stopwatch(); | 68 final _stopwatch = new Stopwatch(); |
| 68 | 69 |
| 69 /// A timer that triggers printing updated time information. | 70 /// Whether we've started [_stopwatch]. |
| 70 Timer _timer; | 71 /// |
| 72 /// We can't just use `_stopwatch.isRunning` because the stopwatch is stopped |
| 73 /// when the reporter is paused. |
| 74 var _stopwatchStarted = false; |
| 71 | 75 |
| 72 /// The size of `_engine.passed` last time a progress notification was | 76 /// The size of `_engine.passed` last time a progress notification was |
| 73 /// printed. | 77 /// printed. |
| 74 int _lastProgressPassed; | 78 int _lastProgressPassed; |
| 75 | 79 |
| 76 /// The size of `_engine.skipped` last time a progress notification was printe
d. | 80 /// The size of `_engine.skipped` last time a progress notification was printe
d. |
| 77 int _lastProgressSkipped; | 81 int _lastProgressSkipped; |
| 78 | 82 |
| 79 /// The size of `_engine.failed` last time a progress notification was | 83 /// The size of `_engine.failed` last time a progress notification was |
| 80 /// printed. | 84 /// printed. |
| 81 int _lastProgressFailed; | 85 int _lastProgressFailed; |
| 82 | 86 |
| 83 /// The duration of the test run in seconds last time a progress notification | 87 /// The duration of the test run in seconds last time a progress notification |
| 84 /// was printed. | 88 /// was printed. |
| 85 int _lastProgressElapsed; | 89 int _lastProgressElapsed; |
| 86 | 90 |
| 87 /// The message printed for the last progress notification. | 91 /// The message printed for the last progress notification. |
| 88 String _lastProgressMessage; | 92 String _lastProgressMessage; |
| 89 | 93 |
| 90 /// Whether the message printed for the last progress notification was | 94 /// Whether the message printed for the last progress notification was |
| 91 /// truncated. | 95 /// truncated. |
| 92 bool _lastProgressTruncated; | 96 bool _lastProgressTruncated; |
| 93 | 97 |
| 94 // Whether a newline has been printed since the last progress line. | 98 // Whether a newline has been printed since the last progress line. |
| 95 var _printedNewline = true; | 99 var _printedNewline = true; |
| 96 | 100 |
| 101 /// Whether the reporter is paused. |
| 102 var _paused = false; |
| 103 |
| 104 /// The set of all subscriptions to various streams. |
| 105 final _subscriptions = new Set<StreamSubscription>(); |
| 106 |
| 97 /// Watches the tests run by [engine] and prints their results to the | 107 /// Watches the tests run by [engine] and prints their results to the |
| 98 /// terminal. | 108 /// terminal. |
| 99 /// | 109 /// |
| 100 /// If [color] is `true`, this will use terminal colors; if it's `false`, it | 110 /// If [color] is `true`, this will use terminal colors; if it's `false`, it |
| 101 /// won't. If [verboseTrace] is `true`, this will print core library frames. | 111 /// won't. If [verboseTrace] is `true`, this will print core library frames. |
| 102 /// If [printPath] is `true`, this will print the path name as part of the | 112 /// If [printPath] is `true`, this will print the path name as part of the |
| 103 /// test description. Likewise, if [printPlatform] is `true`, this will print | 113 /// test description. Likewise, if [printPlatform] is `true`, this will print |
| 104 /// the platform as part of the test description. | 114 /// the platform as part of the test description. |
| 105 static void watch(Engine engine, {bool color: true, bool verboseTrace: false, | 115 static CompactReporter watch(Engine engine, {bool color: true, |
| 106 bool printPath: true, bool printPlatform: true}) { | 116 bool verboseTrace: false, bool printPath: true, |
| 107 new CompactReporter._( | 117 bool printPlatform: true}) { |
| 118 return new CompactReporter._( |
| 108 engine, | 119 engine, |
| 109 color: color, | 120 color: color, |
| 110 verboseTrace: verboseTrace, | 121 verboseTrace: verboseTrace, |
| 111 printPath: printPath, | 122 printPath: printPath, |
| 112 printPlatform: printPlatform); | 123 printPlatform: printPlatform); |
| 113 } | 124 } |
| 114 | 125 |
| 115 CompactReporter._(this._engine, {bool color: true, bool verboseTrace: false, | 126 CompactReporter._(this._engine, {bool color: true, bool verboseTrace: false, |
| 116 bool printPath: true, bool printPlatform: true}) | 127 bool printPath: true, bool printPlatform: true}) |
| 117 : _verboseTrace = verboseTrace, | 128 : _verboseTrace = verboseTrace, |
| 118 _printPath = printPath, | 129 _printPath = printPath, |
| 119 _printPlatform = printPlatform, | 130 _printPlatform = printPlatform, |
| 120 _color = color, | 131 _color = color, |
| 121 _green = color ? '\u001b[32m' : '', | 132 _green = color ? '\u001b[32m' : '', |
| 122 _red = color ? '\u001b[31m' : '', | 133 _red = color ? '\u001b[31m' : '', |
| 123 _yellow = color ? '\u001b[33m' : '', | 134 _yellow = color ? '\u001b[33m' : '', |
| 124 _gray = color ? '\u001b[1;30m' : '', | 135 _gray = color ? '\u001b[1;30m' : '', |
| 125 _bold = color ? '\u001b[1m' : '', | 136 _bold = color ? '\u001b[1m' : '', |
| 126 _noColor = color ? '\u001b[0m' : '' { | 137 _noColor = color ? '\u001b[0m' : '' { |
| 127 _engine.onTestStarted.listen(_onTestStarted); | 138 _subscriptions.add(_engine.onTestStarted.listen(_onTestStarted)); |
| 128 _engine.success.then(_onDone); | 139 |
| 140 /// Convert the future to a stream so that the subscription can be paused or |
| 141 /// canceled. |
| 142 _subscriptions.add(_engine.success.asStream().listen(_onDone)); |
| 143 } |
| 144 |
| 145 void pause() { |
| 146 if (_paused) return; |
| 147 _paused = true; |
| 148 |
| 149 if (!_printedNewline) print(''); |
| 150 _printedNewline = true; |
| 151 _stopwatch.stop(); |
| 152 |
| 153 for (var subscription in _subscriptions) { |
| 154 subscription.pause(); |
| 155 } |
| 156 } |
| 157 |
| 158 void resume() { |
| 159 if (!_paused) return; |
| 160 _paused = false; |
| 161 |
| 162 if (_stopwatchStarted) _stopwatch.start(); |
| 163 |
| 164 for (var subscription in _subscriptions) { |
| 165 subscription.resume(); |
| 166 } |
| 167 } |
| 168 |
| 169 void cancel() { |
| 170 for (var subscription in _subscriptions) { |
| 171 subscription.cancel(); |
| 172 } |
| 173 _subscriptions.clear(); |
| 129 } | 174 } |
| 130 | 175 |
| 131 /// A callback called when the engine begins running [liveTest]. | 176 /// A callback called when the engine begins running [liveTest]. |
| 132 void _onTestStarted(LiveTest liveTest) { | 177 void _onTestStarted(LiveTest liveTest) { |
| 133 if (_timer == null) { | 178 if (!_stopwatchStarted) { |
| 179 _stopwatchStarted = true; |
| 134 _stopwatch.start(); | 180 _stopwatch.start(); |
| 135 /// Keep updating the time even when nothing else is happening. | 181 /// Keep updating the time even when nothing else is happening. |
| 136 _timer = new Timer.periodic(new Duration(seconds: 1), | 182 _subscriptions.add(new Stream.periodic(new Duration(seconds: 1)) |
| 137 (_) => _progressLine(_lastProgressMessage)); | 183 .listen((_) => _progressLine(_lastProgressMessage))); |
| 138 } | 184 } |
| 139 | 185 |
| 140 // If this is the first test to start, print a progress line so the user | 186 // If this is the first test to start, print a progress line so the user |
| 141 // knows what's running. It's possible that the active test may not be | 187 // knows what's running. It's possible that the active test may not be |
| 142 // [liveTest] because the engine doesn't always surface load tests. | 188 // [liveTest] because the engine doesn't always surface load tests. |
| 143 if (_engine.active.length == 1 && _engine.active.first == liveTest) { | 189 if (_engine.active.length == 1 && _engine.active.first == liveTest) { |
| 144 _progressLine(_description(liveTest)); | 190 _progressLine(_description(liveTest)); |
| 145 } | 191 } |
| 146 | 192 |
| 147 liveTest.onStateChange.listen((state) => _onStateChange(liveTest, state)); | 193 _subscriptions.add(liveTest.onStateChange |
| 194 .listen((state) => _onStateChange(liveTest, state))); |
| 148 | 195 |
| 149 liveTest.onError.listen((error) => | 196 _subscriptions.add(liveTest.onError.listen((error) => |
| 150 _onError(liveTest, error.error, error.stackTrace)); | 197 _onError(liveTest, error.error, error.stackTrace))); |
| 151 | 198 |
| 152 liveTest.onPrint.listen((line) { | 199 _subscriptions.add(liveTest.onPrint.listen((line) { |
| 153 _progressLine(_description(liveTest), truncate: false); | 200 _progressLine(_description(liveTest), truncate: false); |
| 154 if (!_printedNewline) print(''); | 201 if (!_printedNewline) print(''); |
| 155 _printedNewline = true; | 202 _printedNewline = true; |
| 156 | 203 |
| 157 print(line); | 204 print(line); |
| 158 }); | 205 })); |
| 159 } | 206 } |
| 160 | 207 |
| 161 /// A callback called when [liveTest]'s state becomes [state]. | 208 /// A callback called when [liveTest]'s state becomes [state]. |
| 162 void _onStateChange(LiveTest liveTest, State state) { | 209 void _onStateChange(LiveTest liveTest, State state) { |
| 163 if (state.status != Status.complete) return; | 210 if (state.status != Status.complete) return; |
| 164 | 211 |
| 165 if (liveTest.test.metadata.skip && | 212 if (liveTest.test.metadata.skip && |
| 166 liveTest.test.metadata.skipReason != null) { | 213 liveTest.test.metadata.skipReason != null) { |
| 167 _progressLine(_description(liveTest)); | 214 _progressLine(_description(liveTest)); |
| 168 print(''); | 215 print(''); |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 203 error.innerError is! String) { | 250 error.innerError is! String) { |
| 204 print(indent(terseChain(stackTrace).toString())); | 251 print(indent(terseChain(stackTrace).toString())); |
| 205 } | 252 } |
| 206 } | 253 } |
| 207 | 254 |
| 208 /// A callback called when the engine is finished running tests. | 255 /// A callback called when the engine is finished running tests. |
| 209 /// | 256 /// |
| 210 /// [success] will be `true` if all tests passed, `false` if some tests | 257 /// [success] will be `true` if all tests passed, `false` if some tests |
| 211 /// failed, and `null` if the engine was closed prematurely. | 258 /// failed, and `null` if the engine was closed prematurely. |
| 212 void _onDone(bool success) { | 259 void _onDone(bool success) { |
| 213 if (_timer != null) _timer.cancel(); | 260 cancel(); |
| 214 _timer = null; | |
| 215 _stopwatch.stop(); | 261 _stopwatch.stop(); |
| 216 | 262 |
| 217 // A null success value indicates that the engine was closed before the | 263 // A null success value indicates that the engine was closed before the |
| 218 // tests finished running, probably because of a signal from the user. We | 264 // tests finished running, probably because of a signal from the user. We |
| 219 // shouldn't print summary information, we should just make sure the | 265 // shouldn't print summary information, we should just make sure the |
| 220 // terminal cursor is on its own line. | 266 // terminal cursor is on its own line. |
| 221 if (success == null) { | 267 if (success == null) { |
| 222 if (!_printedNewline) print(""); | 268 if (!_printedNewline) print(""); |
| 223 _printedNewline = true; | 269 _printedNewline = true; |
| 224 return; | 270 return; |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 332 | 378 |
| 333 if (_printPlatform && liveTest.suite.platform != null) { | 379 if (_printPlatform && liveTest.suite.platform != null) { |
| 334 name = "[${liveTest.suite.platform.name}] $name"; | 380 name = "[${liveTest.suite.platform.name}] $name"; |
| 335 } | 381 } |
| 336 | 382 |
| 337 if (liveTest.suite is LoadSuite) name = "$_bold$_gray$name$_noColor"; | 383 if (liveTest.suite is LoadSuite) name = "$_bold$_gray$name$_noColor"; |
| 338 | 384 |
| 339 return name; | 385 return name; |
| 340 } | 386 } |
| 341 } | 387 } |
| OLD | NEW |