Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(104)

Side by Side Diff: lib/src/console_reporter.dart

Issue 955543002: Move a bunch of src/ files into subdirectories. (Closed) Base URL: git@github.com:dart-lang/unittest@master
Patch Set: Fix library tags Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/src/configuration.dart ('k') | lib/src/dart.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library unittest.console_reporter;
6
7 import 'dart:async';
8 import 'dart:io';
9
10 import 'engine.dart';
11 import 'io.dart';
12 import 'live_test.dart';
13 import 'state.dart';
14 import 'suite.dart';
15 import 'utils.dart';
16
17 /// The terminal escape for green text, or the empty string if this is Windows
18 /// or not outputting to a terminal.
19 final _green = getSpecial('\u001b[32m');
20
21 /// The terminal escape for red text, or the empty string if this is Windows or
22 /// not outputting to a terminal.
23 final _red = getSpecial('\u001b[31m');
24
25 /// The terminal escape for removing test coloring, or the empty string if this
26 /// is Windows or not outputting to a terminal.
27 final _noColor = getSpecial('\u001b[0m');
28
29 /// The maximum console line length.
30 ///
31 /// Lines longer than this will be cropped.
32 const _lineLength = 100;
33
34 /// A reporter that prints test results to the console in a single
35 /// continuously-updating line.
36 class ConsoleReporter {
37 /// The engine used to run the tests.
38 final Engine _engine;
39
40 /// Whether multiple test suites are being run.
41 final bool _multipleSuites;
42
43 /// A stopwatch that tracks the duration of the full run.
44 final _stopwatch = new Stopwatch();
45
46 /// The set of tests that have completed and been marked as passing.
47 final _passed = new Set<LiveTest>();
48
49 /// The set of tests that have completed and been marked as failing or error.
50 final _failed = new Set<LiveTest>();
51
52 /// The size of [_passed] last time a progress notification was printed.
53 int _lastProgressPassed;
54
55 /// The size of [_failed] last time a progress notification was printed.
56 int _lastProgressFailed;
57
58 /// The message printed for the last progress notification.
59 String _lastProgressMessage;
60
61 /// Creates a [ConsoleReporter] that will run all tests in [suites].
62 ConsoleReporter(Iterable<Suite> suites)
63 : _multipleSuites = suites.length > 1,
64 _engine = new Engine(suites) {
65
66 _engine.onTestStarted.listen((liveTest) {
67 _progressLine(_description(liveTest));
68 liveTest.onStateChange.listen((state) {
69 if (state.status != Status.complete) return;
70 if (state.result == Result.success) {
71 _passed.add(liveTest);
72 } else {
73 _passed.remove(liveTest);
74 _failed.add(liveTest);
75 }
76 _progressLine(_description(liveTest));
77 });
78
79 liveTest.onError.listen((error) {
80 if (liveTest.state.status != Status.complete) return;
81
82 _progressLine(_description(liveTest));
83 print('');
84 print(indent(error.error.toString()));
85 print(indent(terseChain(error.stackTrace).toString()));
86 });
87 });
88 }
89
90 /// Runs all tests in all provided suites.
91 ///
92 /// This returns `true` if all tests succeed, and `false` otherwise. It will
93 /// only return once all tests have finished running.
94 Future<bool> run() {
95 if (_stopwatch.isRunning) {
96 throw new StateError("ConsoleReporter.run() may not be called more than "
97 "once.");
98 }
99
100 if (_engine.liveTests.isEmpty) {
101 print("No tests ran.");
102 return new Future.value(true);
103 }
104
105 _stopwatch.start();
106 return _engine.run().then((success) {
107 if (success) {
108 _progressLine("All tests passed!");
109 print('');
110 } else {
111 _progressLine('Some tests failed.', color: _red);
112 print('');
113 }
114
115 return success;
116 });
117 }
118
119 /// Signals that the caller is done with any test output and the reporter
120 /// should release any resources it has allocated.
121 Future close() => _engine.close();
122
123 /// Prints a line representing the current state of the tests.
124 ///
125 /// [message] goes after the progress report, and may be truncated to fit the
126 /// entire line within [_lineLength]. If [color] is passed, it's used as the
127 /// color for [message].
128 void _progressLine(String message, {String color}) {
129 // Print nothing if nothing has changed since the last progress line.
130 if (_passed.length == _lastProgressPassed &&
131 _failed.length == _lastProgressFailed &&
132 message == _lastProgressMessage) {
133 return;
134 }
135
136 _lastProgressPassed = _passed.length;
137 _lastProgressFailed = _failed.length;
138 _lastProgressMessage = message;
139
140 if (color == null) color = '';
141 var duration = _stopwatch.elapsed;
142 var buffer = new StringBuffer();
143
144 // \r moves back to the beginning of the current line.
145 buffer.write('\r${_timeString(duration)} ');
146 buffer.write(_green);
147 buffer.write('+');
148 buffer.write(_passed.length);
149 buffer.write(_noColor);
150
151 if (_failed.isNotEmpty) {
152 buffer.write(_red);
153 buffer.write(' -');
154 buffer.write(_failed.length);
155 buffer.write(_noColor);
156 }
157
158 buffer.write(': ');
159 buffer.write(color);
160
161 // Ensure the line fits within [_lineLength]. [buffer] includes the color
162 // escape sequences too. Because these sequences are not visible characters,
163 // we make sure they are not counted towards the limit.
164 var nonVisible = 1 + _green.length + _noColor.length + color.length +
165 (_failed.isEmpty ? 0 : _red.length + _noColor.length);
166 var length = buffer.length - nonVisible;
167 buffer.write(_truncate(message, _lineLength - length));
168 buffer.write(_noColor);
169
170 // Pad the rest of the line so that it looks erased.
171 length = buffer.length - nonVisible - _noColor.length;
172 buffer.write(' ' * (_lineLength - length));
173 stdout.write(buffer.toString());
174 }
175
176 /// Returns a representation of [duration] as `MM:SS`.
177 String _timeString(Duration duration) {
178 return "${duration.inMinutes.toString().padLeft(2, '0')}:"
179 "${(duration.inSeconds % 60).toString().padLeft(2, '0')}";
180 }
181
182 /// Truncates [text] to fit within [maxLength].
183 ///
184 /// This will try to truncate along word boundaries and preserve words both at
185 /// the beginning and the end of [text].
186 String _truncate(String text, int maxLength) {
187 // Return the full message if it fits.
188 if (text.length <= maxLength) return text;
189
190 // If we can fit the first and last three words, do so.
191 var words = text.split(' ');
192 if (words.length > 1) {
193 var i = words.length;
194 var length = words.first.length + 4;
195 do {
196 i--;
197 length += 1 + words[i].length;
198 } while (length <= maxLength && i > 0);
199 if (length > maxLength || i == 0) i++;
200 if (i < words.length - 4) {
201 // Require at least 3 words at the end.
202 var buffer = new StringBuffer();
203 buffer.write(words.first);
204 buffer.write(' ...');
205 for ( ; i < words.length; i++) {
206 buffer.write(' ');
207 buffer.write(words[i]);
208 }
209 return buffer.toString();
210 }
211 }
212
213 // Otherwise truncate to return the trailing text, but attempt to start at
214 // the beginning of a word.
215 var result = text.substring(text.length - maxLength + 4);
216 var firstSpace = result.indexOf(' ');
217 if (firstSpace > 0) {
218 result = result.substring(firstSpace);
219 }
220 return '...$result';
221 }
222
223 /// Returns a description of [liveTest].
224 ///
225 /// This differs from the test's own description in that it may also include
226 /// the suite's name.
227 String _description(LiveTest liveTest) {
228 if (_multipleSuites) return "${liveTest.suite.name}: ${liveTest.test.name}";
229 return liveTest.test.name;
230 }
231 }
OLDNEW
« no previous file with comments | « lib/src/configuration.dart ('k') | lib/src/dart.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698