Chromium Code Reviews| 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 |