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 // 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 Loading... | |
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 Loading... | |
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 } |
OLD | NEW |