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

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

Issue 1187103004: Allow Suites to be added to an Engine over time. (Closed) Base URL: git@github.com:dart-lang/test@master
Patch Set: Created 5 years, 6 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
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 // TODO(nweiz): This is under lib so that it can be used by the unittest dummy 5 // TODO(nweiz): This is under lib so that it can be used by the unittest dummy
6 // package. Once that package is no longer being updated, move this back into 6 // package. Once that package is no longer being updated, move this back into
7 // bin. 7 // bin.
8 library test.executable; 8 library test.executable;
9 9
10 import 'dart:async'; 10 import 'dart:async';
11 import 'dart:io'; 11 import 'dart:io';
12 import 'dart:math' as math; 12 import 'dart:math' as math;
13 13
14 import 'package:args/args.dart'; 14 import 'package:args/args.dart';
15 import 'package:stack_trace/stack_trace.dart'; 15 import 'package:stack_trace/stack_trace.dart';
16 import 'package:yaml/yaml.dart'; 16 import 'package:yaml/yaml.dart';
17 17
18 import 'backend/metadata.dart'; 18 import 'backend/metadata.dart';
19 import 'backend/test_platform.dart'; 19 import 'backend/test_platform.dart';
20 import 'runner/reporter/compact.dart'; 20 import 'runner/engine.dart';
21 import 'runner/reporter/expanded.dart';
22 import 'runner/application_exception.dart'; 21 import 'runner/application_exception.dart';
23 import 'runner/load_exception.dart'; 22 import 'runner/load_exception.dart';
24 import 'runner/load_exception_suite.dart'; 23 import 'runner/load_exception_suite.dart';
25 import 'runner/loader.dart'; 24 import 'runner/loader.dart';
25 import 'runner/reporter/compact.dart';
26 import 'runner/reporter/expanded.dart';
26 import 'util/exit_codes.dart' as exit_codes; 27 import 'util/exit_codes.dart' as exit_codes;
27 import 'util/io.dart'; 28 import 'util/io.dart';
28 import 'utils.dart'; 29 import 'utils.dart';
29 30
30 /// The argument parser used to parse the executable arguments. 31 /// The argument parser used to parse the executable arguments.
31 final _parser = new ArgParser(allowTrailingOptions: true); 32 final _parser = new ArgParser(allowTrailingOptions: true);
32 33
33 /// The default number of test suites to run at once. 34 /// The default number of test suites to run at once.
34 /// 35 ///
35 /// This defaults to half the available processors, since presumably some of 36 /// This defaults to half the available processors, since presumably some of
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after
159 160
160 transformers: 161 transformers:
161 - test/pub_serve: 162 - test/pub_serve:
162 \$include: test/**_test.dart 163 \$include: test/**_test.dart
163 '''); 164 ''');
164 exitCode = exit_codes.data; 165 exitCode = exit_codes.data;
165 return; 166 return;
166 } 167 }
167 } 168 }
168 169
169 var metadata = new Metadata(verboseTrace: options["verbose-trace"]);
170 var platforms = options["platform"].map(TestPlatform.find);
171 var loader = new Loader(platforms,
172 pubServeUrl: pubServeUrl,
173 packageRoot: options["package-root"],
174 color: color,
175 metadata: metadata,
176 jsTrace: options["js-trace"]);
177
178 var concurrency = _defaultConcurrency; 170 var concurrency = _defaultConcurrency;
179 if (options["concurrency"] != null) { 171 if (options["concurrency"] != null) {
180 try { 172 try {
181 concurrency = int.parse(options["concurrency"]); 173 concurrency = int.parse(options["concurrency"]);
182 } catch (error) { 174 } catch (error) {
183 _printUsage('Couldn\'t parse --concurrency "${options["concurrency"]}":' 175 _printUsage('Couldn\'t parse --concurrency "${options["concurrency"]}":'
184 ' ${error.message}'); 176 ' ${error.message}');
185 exitCode = exit_codes.usage; 177 exitCode = exit_codes.usage;
186 return; 178 return;
187 } 179 }
188 } 180 }
189 181
190 var paths = options.rest; 182 var paths = options.rest;
191 if (paths.isEmpty) { 183 if (paths.isEmpty) {
192 if (!new Directory("test").existsSync()) { 184 if (!new Directory("test").existsSync()) {
193 _printUsage('No test files were passed and the default "test/" ' 185 _printUsage('No test files were passed and the default "test/" '
194 "directory doesn't exist."); 186 "directory doesn't exist.");
195 exitCode = exit_codes.data; 187 exitCode = exit_codes.data;
196 return; 188 return;
197 } 189 }
198 paths = ["test"]; 190 paths = ["test"];
199 } 191 }
200 192
193 var pattern;
194 if (options["name"] != null) {
195 if (options["plain-name"] != null) {
196 _printUsage("--name and --plain-name may not both be passed.");
197 exitCode = exit_codes.data;
198 return;
199 }
200 pattern = new RegExp(options["name"]);
201 } else if (options["plain-name"] != null) {
202 pattern = options["plain-name"];
203 }
204
205 var metadata = new Metadata(verboseTrace: options["verbose-trace"]);
206 var platforms = options["platform"].map(TestPlatform.find);
207 var loader = new Loader(platforms,
208 pubServeUrl: pubServeUrl,
209 packageRoot: options["package-root"],
210 color: color,
211 metadata: metadata,
212 jsTrace: options["js-trace"]);
213
201 var signalSubscription; 214 var signalSubscription;
202 var closed = false;
203 signalSubscription = _signals.listen((_) { 215 signalSubscription = _signals.listen((_) {
204 signalSubscription.cancel(); 216 signalSubscription.cancel();
205 closed = true;
206 loader.close(); 217 loader.close();
207 }); 218 });
208 219
209 try { 220 try {
210 var suites = await mergeStreams(paths.map((path) { 221 var engine = new Engine(concurrency: concurrency);
211 if (new Directory(path).existsSync()) return loader.loadDir(path);
212 if (new File(path).existsSync()) return loader.loadFile(path);
213 return new Stream.fromFuture(new Future.error(
214 new LoadException(path, 'Does not exist.'),
215 new Trace.current()));
216 })).transform(new StreamTransformer.fromHandlers(
217 handleError: (error, stackTrace, sink) {
218 if (error is! LoadException) {
219 sink.addError(error, stackTrace);
220 } else {
221 sink.add(new LoadExceptionSuite(error, stackTrace));
222 }
223 })).toList();
224 222
225 if (closed) return; 223 var watch = options["reporter"] == "compact"
226 suites = flatten(suites); 224 ? CompactReporter.watch
225 : ExpandedReporter.watch;
227 226
228 var pattern; 227 watch(
229 if (options["name"] != null) { 228 engine,
230 if (options["plain-name"] != null) { 229 color: color,
231 _printUsage("--name and --plain-name may not both be passed."); 230 verboseTrace: options["verbose-trace"],
232 exitCode = exit_codes.data; 231 printPath: paths.length > 1 ||
233 return; 232 new Directory(paths.single).existsSync(),
234 } 233 printPlatform: platforms.length > 1);
235 pattern = new RegExp(options["name"]);
236 } else if (options["plain-name"] != null) {
237 pattern = options["plain-name"];
238 }
239
240 if (pattern != null) {
241 suites = suites.map((suite) {
242 // Don't ever filter out load errors.
243 if (suite is LoadExceptionSuite) return suite;
244 return suite.change(
245 tests: suite.tests.where((test) => test.name.contains(pattern)));
246 }).toList();
247
248 if (suites.every((suite) => suite.tests.isEmpty)) {
249 stderr.write('No tests match ');
250
251 if (pattern is RegExp) {
252 stderr.writeln('regular expression "${pattern.pattern}".');
253 } else {
254 stderr.writeln('"$pattern".');
255 }
256 exitCode = exit_codes.data;
257 return;
258 }
259 }
260
261 var reporter = options["reporter"] == "compact"
262 ? new CompactReporter(
263 flatten(suites),
264 concurrency: concurrency,
265 color: color,
266 verboseTrace: options["verbose-trace"])
267 : new ExpandedReporter(
268 flatten(suites),
269 concurrency: concurrency,
270 color: color,
271 verboseTrace: options["verbose-trace"]);
272 234
273 // Override the signal handler to close [reporter]. [loader] will still be 235 // Override the signal handler to close [reporter]. [loader] will still be
274 // closed in the [whenComplete] below. 236 // closed in the [whenComplete] below.
275 signalSubscription.onData((_) { 237 signalSubscription.onData((_) async {
276 signalSubscription.cancel(); 238 signalSubscription.cancel();
277 closed = true;
278 239
279 // Wait a bit to print this message, since printing it eagerly looks weird 240 // Wait a bit to print this message, since printing it eagerly looks weird
280 // if the tests then finish immediately. 241 // if the tests then finish immediately.
281 var timer = new Timer(new Duration(seconds: 1), () { 242 var timer = new Timer(new Duration(seconds: 1), () {
282 // Print a blank line first to ensure that this doesn't interfere with 243 // Print a blank line first to ensure that this doesn't interfere with
283 // the compact reporter's unfinished line. 244 // the compact reporter's unfinished line.
284 print(""); 245 print("");
285 print("Waiting for current test(s) to finish."); 246 print("Waiting for current test(s) to finish.");
286 print("Press Control-C again to terminate immediately."); 247 print("Press Control-C again to terminate immediately.");
287 }); 248 });
288 249
289 reporter.close().then((_) => timer.cancel()); 250 await engine.close();
251 timer.cancel();
252 await loader.close();
290 }); 253 });
291 254
292 try { 255 try {
293 var success = await reporter.run(); 256 var results = await Future.wait([
294 exitCode = success ? 0 : 1; 257 _loadSuites(paths, pattern, loader, engine),
258 engine.run()
259 ], eagerError: true);
260
261 // Explicitly check "== true" here because [engine.run] can return `null`
262 // if the engine was closed prematurely.
263 exitCode = results.last == true ? 0 : 1;
295 } finally { 264 } finally {
296 signalSubscription.cancel(); 265 signalSubscription.cancel();
297 await reporter.close(); 266 await engine.close();
267 }
268
269 if (engine.passed.length == 0 && engine.failed.length == 0 &&
270 engine.skipped.length == 0 && pattern != null) {
271 stderr.write('No tests match ');
272
273 if (pattern is RegExp) {
274 stderr.writeln('regular expression "${pattern.pattern}".');
275 } else {
276 stderr.writeln('"$pattern".');
277 }
278 exitCode = exit_codes.data;
298 } 279 }
299 } on ApplicationException catch (error) { 280 } on ApplicationException catch (error) {
300 stderr.writeln(error.message); 281 stderr.writeln(error.message);
301 exitCode = exit_codes.data; 282 exitCode = exit_codes.data;
302 } catch (error, stackTrace) { 283 } catch (error, stackTrace) {
303 stderr.writeln(getErrorMessage(error)); 284 stderr.writeln(getErrorMessage(error));
304 stderr.writeln(new Trace.from(stackTrace).terse); 285 stderr.writeln(new Trace.from(stackTrace).terse);
305 stderr.writeln( 286 stderr.writeln(
306 "This is an unexpected error. Please file an issue at " 287 "This is an unexpected error. Please file an issue at "
307 "http://github.com/dart-lang/test\n" 288 "http://github.com/dart-lang/test\n"
308 "with the stack trace and instructions for reproducing the error."); 289 "with the stack trace and instructions for reproducing the error.");
309 exitCode = exit_codes.software; 290 exitCode = exit_codes.software;
310 } finally { 291 } finally {
311 signalSubscription.cancel(); 292 signalSubscription.cancel();
312 await loader.close(); 293 await loader.close();
313 } 294 }
314 } 295 }
315 296
297 /// Load the test suites in [paths] that match [pattern] and pass them to
298 /// [engine].
299 ///
300 /// This completes once all the tests have been added to the engine. It does not
301 /// run the engine.
302 Future _loadSuites(List<String> paths, Pattern pattern, Loader loader,
303 Engine engine) async {
304 var completer = new Completer();
305
306 mergeStreams(paths.map((path) {
307 if (new Directory(path).existsSync()) return loader.loadDir(path);
308 if (new File(path).existsSync()) return loader.loadFile(path);
309
310 return new Stream.fromFuture(new Future.error(
311 new LoadException(path, 'Does not exist.'),
312 new Trace.current()));
313 })).map((suite) {
314 if (pattern == null) return suite;
315 return suite.change(
316 tests: suite.tests.where((test) => test.name.contains(pattern)));
317 }).listen((suite) => engine.suiteSink.add(suite),
kevmoo 2015/06/17 22:20:04 consider using await async here – won't need a com
nweiz 2015/06/17 22:41:08 We can't use it here, because we need to intercept
318 onError: (error, stackTrace) {
319 if (error is LoadException) {
320 engine.suiteSink.add(new LoadExceptionSuite(error, stackTrace));
321 } else if (!completer.isCompleted) {
322 completer.completeError(error, stackTrace);
323 }
324 }, onDone: () => completer.complete());
325
326 await completer.future;
327
328 // Once we've loaded all the suites, notify the engine that no more will be
329 // coming.
330 engine.suiteSink.close();
331 }
332
316 /// Print usage information for this command. 333 /// Print usage information for this command.
317 /// 334 ///
318 /// If [error] is passed, it's used in place of the usage message and the whole 335 /// If [error] is passed, it's used in place of the usage message and the whole
319 /// thing is printed to stderr instead of stdout. 336 /// thing is printed to stderr instead of stdout.
320 void _printUsage([String error]) { 337 void _printUsage([String error]) {
321 var output = stdout; 338 var output = stdout;
322 339
323 var message = "Runs tests in this package."; 340 var message = "Runs tests in this package.";
324 if (error != null) { 341 if (error != null) {
325 message = error; 342 message = error;
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
384 if (description is! Map) return false; 401 if (description is! Map) return false;
385 var path = description["path"]; 402 var path = description["path"];
386 if (path is! String) return false; 403 if (path is! String) return false;
387 404
388 print("$version (from $path)"); 405 print("$version (from $path)");
389 return true; 406 return true;
390 407
391 default: return false; 408 default: return false;
392 } 409 }
393 } 410 }
OLDNEW
« no previous file with comments | « no previous file | lib/src/runner/browser/browser.dart » ('j') | lib/src/runner/browser/content_shell.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698