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

Side by Side Diff: mojo/public/dart/third_party/test/lib/src/runner.dart

Issue 1346773002: Stop running pub get at gclient sync time and fix build bugs (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 5 years, 3 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
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 test.runner;
6
7 import 'dart:async';
8 import 'dart:io';
9
10 import 'package:async/async.dart';
11
12 import 'backend/test_platform.dart';
13 import 'runner/application_exception.dart';
14 import 'runner/configuration.dart';
15 import 'runner/engine.dart';
16 import 'runner/load_exception.dart';
17 import 'runner/load_suite.dart';
18 import 'runner/loader.dart';
19 import 'runner/reporter.dart';
20 import 'runner/reporter/compact.dart';
21 import 'runner/reporter/expanded.dart';
22 import 'runner/runner_suite.dart';
23 import 'util/io.dart';
24 import 'utils.dart';
25
26 /// A class that loads and runs tests based on a [Configuration].
27 ///
28 /// This maintains a [Loader] and an [Engine] and passes test suites from one to
29 /// the other, as well as printing out tests with a [CompactReporter] or an
30 /// [ExpandedReporter].
31 class Runner {
32 /// The configuration for the runner.
33 final Configuration _config;
34
35 /// The loader that loads the test suites from the filesystem.
36 final Loader _loader;
37
38 /// The engine that runs the test suites.
39 final Engine _engine;
40
41 /// The reporter that's emitting the test runner's results.
42 final Reporter _reporter;
43
44 /// The subscription to the stream returned by [_loadSuites].
45 StreamSubscription _suiteSubscription;
46
47 /// The memoizer for ensuring [close] only runs once.
48 final _closeMemo = new AsyncMemoizer();
49 bool get _closed => _closeMemo.hasRun;
50
51 /// Creates a new runner based on [configuration].
52 factory Runner(Configuration config) {
53 var loader = new Loader(config);
54 var engine = new Engine(concurrency: config.concurrency);
55
56 var watch = config.reporter == "compact"
57 ? CompactReporter.watch
58 : ExpandedReporter.watch;
59
60 var reporter = watch(
61 engine,
62 color: config.color,
63 verboseTrace: config.verboseTrace,
64 printPath: config.paths.length > 1 ||
65 new Directory(config.paths.single).existsSync(),
66 printPlatform: config.platforms.length > 1);
67
68 return new Runner._(config, loader, engine, reporter);
69 }
70
71 Runner._(this._config, this._loader, this._engine, this._reporter);
72
73 /// Starts the runner.
74 ///
75 /// This starts running tests and printing their progress. It returns whether
76 /// or not they ran successfully.
77 Future<bool> run() async {
78 if (_closed) {
79 throw new StateError("run() may not be called on a closed Runner.");
80 }
81
82 var suites = _loadSuites();
83
84 var success;
85 if (_config.pauseAfterLoad) {
86 success = await _loadThenPause(suites);
87 } else {
88 _suiteSubscription = suites.listen(_engine.suiteSink.add);
89 var results = await Future.wait([
90 _suiteSubscription.asFuture().then((_) => _engine.suiteSink.close()),
91 _engine.run()
92 ], eagerError: true);
93 success = results.last;
94 }
95
96 if (_closed) return false;
97
98 if (_engine.passed.length == 0 && _engine.failed.length == 0 &&
99 _engine.skipped.length == 0 && _config.pattern != null) {
100 var message = 'No tests match ';
101
102 if (_config.pattern is RegExp) {
103 var pattern = (_config.pattern as RegExp).pattern;
104 message += 'regular expression "$pattern".';
105 } else {
106 message += '"${_config.pattern}".';
107 }
108 throw new ApplicationException(message);
109 }
110
111 // Explicitly check "== true" here because [Engine.run] can return `null`
112 // if the engine was closed prematurely.
113 return success == true;
114 }
115
116 /// Closes the runner.
117 ///
118 /// This stops any future test suites from running. It will wait for any
119 /// currently-running VM tests, in case they have stuff to clean up on the
120 /// filesystem.
121 Future close() => _closeMemo.runOnce(() async {
122 var timer;
123 if (!_engine.isIdle) {
124 // Wait a bit to print this message, since printing it eagerly looks weird
125 // if the tests then finish immediately.
126 timer = new Timer(new Duration(seconds: 1), () {
127 // Pause the reporter while we print to ensure that we don't interfere
128 // with its output.
129 _reporter.pause();
130 print("Waiting for current test(s) to finish.");
131 print("Press Control-C again to terminate immediately.");
132 _reporter.resume();
133 });
134 }
135
136 if (_suiteSubscription != null) _suiteSubscription.cancel();
137 _suiteSubscription = null;
138
139 // Make sure we close the engine *before* the loader. Otherwise,
140 // LoadSuites provided by the loader may get into bad states.
141 await _engine.close();
142 if (timer != null) timer.cancel();
143 await _loader.close();
144 });
145
146 /// Return a stream of [LoadSuite]s in [_config.paths].
147 ///
148 /// Only tests that match [_config.pattern] will be included in the
149 /// suites once they're loaded.
150 Stream<LoadSuite> _loadSuites() {
151 return mergeStreams(_config.paths.map((path) {
152 if (new Directory(path).existsSync()) return _loader.loadDir(path);
153 if (new File(path).existsSync()) return _loader.loadFile(path);
154
155 return new Stream.fromIterable([
156 new LoadSuite("loading $path", () =>
157 throw new LoadException(path, 'Does not exist.'))
158 ]);
159 })).map((loadSuite) {
160 return loadSuite.changeSuite((suite) {
161 if (_config.pattern == null) return suite;
162 return suite.change(tests: suite.tests.where((test) =>
163 test.name.contains(_config.pattern)));
164 });
165 });
166 }
167
168 /// Loads each suite in [suites] in order, pausing after load for platforms
169 /// that support debugging.
170 Future<bool> _loadThenPause(Stream<LoadSuite> suites) async {
171 if (_config.platforms.contains(TestPlatform.vm)) {
172 warn("Debugging is currently unsupported on the Dart VM.",
173 color: _config.color);
174 }
175
176 _suiteSubscription = suites.asyncMap((loadSuite) async {
177 // Make the underlying suite null so that the engine doesn't start running
178 // it immediately.
179 _engine.suiteSink.add(loadSuite.changeSuite((_) => null));
180
181 var suite = await loadSuite.suite;
182 if (suite == null) return;
183
184 await _pause(suite);
185 if (_closed) return;
186
187 _engine.suiteSink.add(suite);
188 await _engine.onIdle.first;
189 }).listen(null);
190
191 var results = await Future.wait([
192 _suiteSubscription.asFuture().then((_) => _engine.suiteSink.close()),
193 _engine.run()
194 ]);
195 return results.last;
196 }
197
198 /// Pauses the engine and the reporter so that the user can set breakpoints as
199 /// necessary.
200 ///
201 /// This is a no-op for test suites that aren't on platforms where debugging
202 /// is supported.
203 Future _pause(RunnerSuite suite) async {
204 if (suite.platform == null) return;
205 if (suite.platform == TestPlatform.vm) return;
206
207 try {
208 _reporter.pause();
209
210 var bold = _config.color ? '\u001b[1m' : '';
211 var yellow = _config.color ? '\u001b[33m' : '';
212 var noColor = _config.color ? '\u001b[0m' : '';
213 print('');
214
215 if (suite.platform.isDartVM) {
216 var url = suite.environment.observatoryUrl;
217 if (url == null) {
218 print("${yellow}Observatory URL not found. Make sure you're using "
219 "${suite.platform.name} 1.11 or later.$noColor");
220 } else {
221 print("Observatory URL: $bold$url$noColor");
222 }
223 }
224
225 if (suite.platform.isHeadless) {
226 var url = suite.environment.remoteDebuggerUrl;
227 if (url == null) {
228 print("${yellow}Remote debugger URL not found.$noColor");
229 } else {
230 print("Remote debugger URL: $bold$url$noColor");
231 }
232 }
233
234 var buffer = new StringBuffer(
235 "${bold}The test runner is paused.${noColor} ");
236 if (!suite.platform.isHeadless) {
237 buffer.write("Open the dev console in ${suite.platform} ");
238 } else {
239 buffer.write("Open the remote debugger ");
240 }
241 if (suite.platform.isDartVM) buffer.write("or the Observatory ");
242
243 buffer.write("and set breakpoints. Once you're finished, return to this "
244 "terminal and press Enter.");
245
246 print(wordWrap(buffer.toString()));
247
248 await inCompletionOrder([
249 suite.environment.displayPause(),
250 cancelableNext(stdinLines)
251 ]).first;
252 } finally {
253 _reporter.resume();
254 }
255 }
256 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698