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

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

Issue 1239613002: Factor out a Runner class from executable.dart. (Closed) Base URL: git@github.com:dart-lang/test@master
Patch Set: Created 5 years, 5 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 | « no previous file | lib/src/runner.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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';
11 import 'dart:io'; 10 import 'dart:io';
12 11
13 import 'package:async/async.dart';
14 import 'package:stack_trace/stack_trace.dart'; 12 import 'package:stack_trace/stack_trace.dart';
15 import 'package:yaml/yaml.dart'; 13 import 'package:yaml/yaml.dart';
16 14
17 import 'backend/metadata.dart'; 15 import 'runner.dart';
18 import 'runner/application_exception.dart'; 16 import 'runner/application_exception.dart';
19 import 'runner/configuration.dart'; 17 import 'runner/configuration.dart';
20 import 'runner/engine.dart';
21 import 'runner/load_exception.dart';
22 import 'runner/load_suite.dart';
23 import 'runner/loader.dart';
24 import 'runner/reporter/compact.dart';
25 import 'runner/reporter/expanded.dart';
26 import 'util/exit_codes.dart' as exit_codes; 18 import 'util/exit_codes.dart' as exit_codes;
19 import 'util/io.dart';
kevmoo 2015/07/14 01:44:16 unused import
nweiz 2015/07/14 19:42:51 Done.
27 import 'utils.dart'; 20 import 'utils.dart';
28 21
29 /// A merged stream of all signals that tell the test runner to shut down 22 /// A merged stream of all signals that tell the test runner to shut down
30 /// gracefully. 23 /// gracefully.
31 /// 24 ///
32 /// Signals will only be captured as long as this has an active subscription. 25 /// Signals will only be captured as long as this has an active subscription.
33 /// Otherwise, they'll be handled by Dart's default signal handler, which 26 /// Otherwise, they'll be handled by Dart's default signal handler, which
34 /// terminates the program immediately. 27 /// terminates the program immediately.
35 final _signals = Platform.isWindows 28 final _signals = Platform.isWindows
36 ? ProcessSignal.SIGINT.watch() 29 ? ProcessSignal.SIGINT.watch()
(...skipping 22 matching lines...) Expand all
59 if (transformers is! List) return false; 52 if (transformers is! List) return false;
60 53
61 return transformers.any((transformer) { 54 return transformers.any((transformer) {
62 if (transformer is String) return transformer == 'test/pub_serve'; 55 if (transformer is String) return transformer == 'test/pub_serve';
63 if (transformer is! Map) return false; 56 if (transformer is! Map) return false;
64 if (transformer.keys.length != 1) return false; 57 if (transformer.keys.length != 1) return false;
65 return transformer.keys.single == 'test/pub_serve'; 58 return transformer.keys.single == 'test/pub_serve';
66 }); 59 });
67 } 60 }
68 61
69 Configuration _configuration;
70
71 main(List<String> args) async { 62 main(List<String> args) async {
63 var configuration;
72 try { 64 try {
73 _configuration = new Configuration.parse(args); 65 configuration = new Configuration.parse(args);
74 } on FormatException catch (error) { 66 } on FormatException catch (error) {
75 _printUsage(error.message); 67 _printUsage(error.message);
76 exitCode = exit_codes.usage; 68 exitCode = exit_codes.usage;
77 return; 69 return;
78 } 70 }
79 71
80 if (_configuration.help) { 72 if (configuration.help) {
81 _printUsage(); 73 _printUsage();
82 return; 74 return;
83 } 75 }
84 76
85 if (_configuration.version) { 77 if (configuration.version) {
86 if (!_printVersion()) { 78 if (!_printVersion()) {
87 stderr.writeln("Couldn't find version number."); 79 stderr.writeln("Couldn't find version number.");
88 exitCode = exit_codes.data; 80 exitCode = exit_codes.data;
89 } 81 }
90 return; 82 return;
91 } 83 }
92 84
93 if (_configuration.pubServeUrl != null && !_usesTransformer) { 85 if (configuration.pubServeUrl != null && !_usesTransformer) {
94 stderr.write(''' 86 stderr.write('''
95 When using --pub-serve, you must include the "test/pub_serve" transformer in 87 When using --pub-serve, you must include the "test/pub_serve" transformer in
96 your pubspec: 88 your pubspec:
97 89
98 transformers: 90 transformers:
99 - test/pub_serve: 91 - test/pub_serve:
100 \$include: test/**_test.dart 92 \$include: test/**_test.dart
101 '''); 93 ''');
102 exitCode = exit_codes.data; 94 exitCode = exit_codes.data;
103 return; 95 return;
104 } 96 }
105 97
106 if (!_configuration.explicitPaths && 98 if (!configuration.explicitPaths &&
107 !new Directory(_configuration.paths.single).existsSync()) { 99 !new Directory(configuration.paths.single).existsSync()) {
108 _printUsage('No test files were passed and the default "test/" ' 100 _printUsage('No test files were passed and the default "test/" '
109 "directory doesn't exist."); 101 "directory doesn't exist.");
110 exitCode = exit_codes.data; 102 exitCode = exit_codes.data;
111 return; 103 return;
112 } 104 }
113 105
114 var metadata = new Metadata( 106 var runner = new Runner(configuration);
115 verboseTrace: _configuration.verboseTrace);
116 var loader = new Loader(_configuration.platforms,
117 pubServeUrl: _configuration.pubServeUrl,
118 packageRoot: _configuration.packageRoot,
119 color: _configuration.color,
120 metadata: metadata,
121 jsTrace: _configuration.jsTrace);
122 107
123 var closed = false;
124 var signalSubscription; 108 var signalSubscription;
125 signalSubscription = _signals.listen((_) { 109 close() async {
126 closed = true; 110 if (signalSubscription == null) return;
127 signalSubscription.cancel(); 111 signalSubscription.cancel();
128 loader.close(); 112 signalSubscription = null;
129 }); 113 await runner.close();
114 }
115
116 signalSubscription = _signals.listen((_) => close());
130 117
131 try { 118 try {
132 var engine = new Engine(concurrency: _configuration.concurrency); 119 exitCode = (await runner.run()) ? 0 : 1;
133
134 var watch = _configuration.reporter == "compact"
135 ? CompactReporter.watch
136 : ExpandedReporter.watch;
137
138 watch(
139 engine,
140 color: _configuration.color,
141 verboseTrace: _configuration.verboseTrace,
142 printPath: _configuration.paths.length > 1 ||
143 new Directory(_configuration.paths.single).existsSync(),
144 printPlatform: _configuration.platforms.length > 1);
145
146 // Override the signal handler to close [reporter]. [loader] will still be
147 // closed in the [whenComplete] below.
148 signalSubscription.onData((_) async {
149 closed = true;
150 signalSubscription.cancel();
151
152 // Wait a bit to print this message, since printing it eagerly looks weird
153 // if the tests then finish immediately.
154 var timer = new Timer(new Duration(seconds: 1), () {
155 // Print a blank line first to ensure that this doesn't interfere with
156 // the compact reporter's unfinished line.
157 print("");
158 print("Waiting for current test(s) to finish.");
159 print("Press Control-C again to terminate immediately.");
160 });
161
162 // Make sure we close the engine *before* the loader. Otherwise,
163 // LoadSuites provided by the loader may get into bad states.
164 await engine.close();
165 timer.cancel();
166 await loader.close();
167 });
168
169 try {
170 var results = await Future.wait([
171 _loadSuites(loader, engine),
172 engine.run()
173 ], eagerError: true);
174
175 if (closed) return;
176
177 // Explicitly check "== true" here because [engine.run] can return `null`
178 // if the engine was closed prematurely.
179 exitCode = results.last == true ? 0 : 1;
180 } finally {
181 signalSubscription.cancel();
182 await engine.close();
183 }
184
185 if (engine.passed.length == 0 && engine.failed.length == 0 &&
186 engine.skipped.length == 0 && _configuration.pattern != null) {
187 stderr.write('No tests match ');
188
189 if (_configuration.pattern is RegExp) {
190 var pattern = (_configuration.pattern as RegExp).pattern;
191 stderr.writeln('regular expression "$pattern".');
192 } else {
193 stderr.writeln('"${_configuration.pattern}".');
194 }
195 exitCode = exit_codes.data;
196 }
197 } on ApplicationException catch (error) { 120 } on ApplicationException catch (error) {
198 stderr.writeln(error.message); 121 stderr.writeln(error.message);
199 exitCode = exit_codes.data; 122 exitCode = exit_codes.data;
200 } catch (error, stackTrace) { 123 } catch (error, stackTrace) {
201 stderr.writeln(getErrorMessage(error)); 124 stderr.writeln(getErrorMessage(error));
202 stderr.writeln(new Trace.from(stackTrace).terse); 125 stderr.writeln(new Trace.from(stackTrace).terse);
203 stderr.writeln( 126 stderr.writeln(
204 "This is an unexpected error. Please file an issue at " 127 "This is an unexpected error. Please file an issue at "
205 "http://github.com/dart-lang/test\n" 128 "http://github.com/dart-lang/test\n"
206 "with the stack trace and instructions for reproducing the error."); 129 "with the stack trace and instructions for reproducing the error.");
207 exitCode = exit_codes.software; 130 exitCode = exit_codes.software;
208 } finally { 131 } finally {
209 signalSubscription.cancel(); 132 await close();
210 await loader.close();
211 } 133 }
212 } 134 }
213 135
214 /// Load the test suites in [_configuration.paths] that match
215 /// [_configuration.pattern] and pass them to [engine].
216 ///
217 /// This completes once all the tests have been added to the engine. It does not
218 /// run the engine.
219 Future _loadSuites(Loader loader, Engine engine) async {
220 var group = new FutureGroup();
221
222 mergeStreams(_configuration.paths.map((path) {
223 if (new Directory(path).existsSync()) return loader.loadDir(path);
224 if (new File(path).existsSync()) return loader.loadFile(path);
225
226 return new Stream.fromIterable([
227 new LoadSuite("loading $path", () =>
228 throw new LoadException(path, 'Does not exist.'))
229 ]);
230 })).listen((loadSuite) {
231 group.add(new Future.sync(() {
232 engine.suiteSink.add(loadSuite.changeSuite((suite) {
233 if (_configuration.pattern == null) return suite;
234 return suite.change(tests: suite.tests.where(
235 (test) => test.name.contains(_configuration.pattern)));
236 }));
237 }));
238 }, onError: (error, stackTrace) {
239 group.add(new Future.error(error, stackTrace));
240 }, onDone: group.close);
241
242 await group.future;
243
244 // Once we've loaded all the suites, notify the engine that no more will be
245 // coming.
246 engine.suiteSink.close();
247 }
248
249 /// Print usage information for this command. 136 /// Print usage information for this command.
250 /// 137 ///
251 /// If [error] is passed, it's used in place of the usage message and the whole 138 /// If [error] is passed, it's used in place of the usage message and the whole
252 /// thing is printed to stderr instead of stdout. 139 /// thing is printed to stderr instead of stdout.
253 void _printUsage([String error]) { 140 void _printUsage([String error]) {
254 var output = stdout; 141 var output = stdout;
255 142
256 var message = "Runs tests in this package."; 143 var message = "Runs tests in this package.";
257 if (error != null) { 144 if (error != null) {
258 message = error; 145 message = error;
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
317 if (description is! Map) return false; 204 if (description is! Map) return false;
318 var path = description["path"]; 205 var path = description["path"];
319 if (path is! String) return false; 206 if (path is! String) return false;
320 207
321 print("$version (from $path)"); 208 print("$version (from $path)");
322 return true; 209 return true;
323 210
324 default: return false; 211 default: return false;
325 } 212 }
326 } 213 }
OLDNEW
« no previous file with comments | « no previous file | lib/src/runner.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698