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 |