| 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'; | 10 import 'dart:async'; |
| 11 import 'dart:io'; | 11 import 'dart:io'; |
| 12 import 'dart:isolate'; | 12 import 'dart:isolate'; |
| 13 import 'dart:math' as math; |
| 13 | 14 |
| 14 import 'package:args/args.dart'; | 15 import 'package:args/args.dart'; |
| 15 import 'package:stack_trace/stack_trace.dart'; | 16 import 'package:stack_trace/stack_trace.dart'; |
| 16 import 'package:yaml/yaml.dart'; | 17 import 'package:yaml/yaml.dart'; |
| 17 | 18 |
| 18 import 'backend/test_platform.dart'; | 19 import 'backend/test_platform.dart'; |
| 19 import 'runner/reporter/compact.dart'; | 20 import 'runner/reporter/compact.dart'; |
| 20 import 'runner/load_exception.dart'; | 21 import 'runner/load_exception.dart'; |
| 21 import 'runner/loader.dart'; | 22 import 'runner/loader.dart'; |
| 22 import 'util/exit_codes.dart' as exit_codes; | 23 import 'util/exit_codes.dart' as exit_codes; |
| 23 import 'util/io.dart'; | 24 import 'util/io.dart'; |
| 24 import 'utils.dart'; | 25 import 'utils.dart'; |
| 25 | 26 |
| 26 /// The argument parser used to parse the executable arguments. | 27 /// The argument parser used to parse the executable arguments. |
| 27 final _parser = new ArgParser(allowTrailingOptions: true); | 28 final _parser = new ArgParser(allowTrailingOptions: true); |
| 28 | 29 |
| 30 /// The default number of test suites to run at once. |
| 31 /// |
| 32 /// This defaults to half the available processors, since presumably some of |
| 33 /// them will be used for the OS and other processes. |
| 34 final _defaultConcurrency = math.max(1, Platform.numberOfProcessors ~/ 2); |
| 35 |
| 29 /// A merged stream of all signals that tell the test runner to shut down | 36 /// A merged stream of all signals that tell the test runner to shut down |
| 30 /// gracefully. | 37 /// gracefully. |
| 31 /// | 38 /// |
| 32 /// Signals will only be captured as long as this has an active subscription. | 39 /// 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 | 40 /// Otherwise, they'll be handled by Dart's default signal handler, which |
| 34 /// terminates the program immediately. | 41 /// terminates the program immediately. |
| 35 final _signals = mergeStreams([ | 42 final _signals = mergeStreams([ |
| 36 ProcessSignal.SIGTERM.watch(), ProcessSignal.SIGINT.watch() | 43 ProcessSignal.SIGTERM.watch(), ProcessSignal.SIGINT.watch() |
| 37 ]); | 44 ]); |
| 38 | 45 |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 73 'Regular expression syntax is supported.'); | 80 'Regular expression syntax is supported.'); |
| 74 _parser.addOption("plain-name", | 81 _parser.addOption("plain-name", |
| 75 abbr: 'N', | 82 abbr: 'N', |
| 76 help: 'A plain-text substring of the name of the test to run.'); | 83 help: 'A plain-text substring of the name of the test to run.'); |
| 77 _parser.addOption("platform", | 84 _parser.addOption("platform", |
| 78 abbr: 'p', | 85 abbr: 'p', |
| 79 help: 'The platform(s) on which to run the tests.', | 86 help: 'The platform(s) on which to run the tests.', |
| 80 allowed: TestPlatform.all.map((platform) => platform.identifier).toList(), | 87 allowed: TestPlatform.all.map((platform) => platform.identifier).toList(), |
| 81 defaultsTo: 'vm', | 88 defaultsTo: 'vm', |
| 82 allowMultiple: true); | 89 allowMultiple: true); |
| 90 _parser.addOption("concurrency", |
| 91 abbr: 'j', |
| 92 help: 'The number of concurrent test suites run.\n' |
| 93 '(defaults to $_defaultConcurrency)', |
| 94 valueHelp: 'threads'); |
| 83 _parser.addOption("pub-serve", | 95 _parser.addOption("pub-serve", |
| 84 help: 'The port of a pub serve instance serving "test/".', | 96 help: 'The port of a pub serve instance serving "test/".', |
| 85 hide: !supportsPubServe, | 97 hide: !supportsPubServe, |
| 86 valueHelp: 'port'); | 98 valueHelp: 'port'); |
| 87 _parser.addFlag("color", defaultsTo: null, | 99 _parser.addFlag("color", defaultsTo: null, |
| 88 help: 'Whether to use terminal colors.\n(auto-detected by default)'); | 100 help: 'Whether to use terminal colors.\n(auto-detected by default)'); |
| 89 | 101 |
| 90 var options; | 102 var options; |
| 91 try { | 103 try { |
| 92 options = _parser.parse(args); | 104 options = _parser.parse(args); |
| (...skipping 27 matching lines...) Expand all Loading... |
| 120 return; | 132 return; |
| 121 } | 133 } |
| 122 } | 134 } |
| 123 | 135 |
| 124 var platforms = options["platform"].map(TestPlatform.find); | 136 var platforms = options["platform"].map(TestPlatform.find); |
| 125 var loader = new Loader(platforms, | 137 var loader = new Loader(platforms, |
| 126 pubServeUrl: pubServeUrl, | 138 pubServeUrl: pubServeUrl, |
| 127 packageRoot: options["package-root"], | 139 packageRoot: options["package-root"], |
| 128 color: color); | 140 color: color); |
| 129 | 141 |
| 142 var concurrency = _defaultConcurrency; |
| 143 if (options["concurrency"] != null) { |
| 144 try { |
| 145 concurrency = int.parse(options["concurrency"]); |
| 146 } catch (error) { |
| 147 _printUsage('Couldn\'t parse --concurrency "${options["concurrency"]}":' |
| 148 ' ${error.message}'); |
| 149 exitCode = exit_codes.usage; |
| 150 return; |
| 151 } |
| 152 } |
| 153 |
| 130 var signalSubscription; | 154 var signalSubscription; |
| 131 var closed = false; | 155 var closed = false; |
| 132 signalSubscription = _signals.listen((_) { | 156 signalSubscription = _signals.listen((_) { |
| 133 signalSubscription.cancel(); | 157 signalSubscription.cancel(); |
| 134 closed = true; | 158 closed = true; |
| 135 loader.close(); | 159 loader.close(); |
| 136 }); | 160 }); |
| 137 | 161 |
| 138 new Future.sync(() { | 162 new Future.sync(() { |
| 139 var paths = options.rest; | 163 var paths = options.rest; |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 179 if (pattern is RegExp) { | 203 if (pattern is RegExp) { |
| 180 stderr.write('regular expression "${pattern.pattern}".'); | 204 stderr.write('regular expression "${pattern.pattern}".'); |
| 181 } else { | 205 } else { |
| 182 stderr.writeln('"$pattern".'); | 206 stderr.writeln('"$pattern".'); |
| 183 } | 207 } |
| 184 exitCode = exit_codes.data; | 208 exitCode = exit_codes.data; |
| 185 return null; | 209 return null; |
| 186 } | 210 } |
| 187 } | 211 } |
| 188 | 212 |
| 189 var reporter = new CompactReporter(flatten(suites), color: color); | 213 var reporter = new CompactReporter(flatten(suites), |
| 214 concurrency: concurrency, color: color); |
| 190 | 215 |
| 191 // Override the signal handler to close [reporter]. [loader] will still be | 216 // Override the signal handler to close [reporter]. [loader] will still be |
| 192 // closed in the [whenComplete] below. | 217 // closed in the [whenComplete] below. |
| 193 signalSubscription.onData((_) { | 218 signalSubscription.onData((_) { |
| 194 signalSubscription.cancel(); | 219 signalSubscription.cancel(); |
| 195 closed = true; | 220 closed = true; |
| 196 | 221 |
| 197 // Wait a bit to print this message, since printing it eagerly looks weird | 222 // Wait a bit to print this message, since printing it eagerly looks weird |
| 198 // if the tests then finish immediately. | 223 // if the tests then finish immediately. |
| 199 var timer = new Timer(new Duration(seconds: 1), () { | 224 var timer = new Timer(new Duration(seconds: 1), () { |
| 200 print("Waiting for current test to finish."); | 225 // Print a blank line first to ensure that this doesn't interfere with |
| 226 // the compact reporter's unfinished line. |
| 227 print(""); |
| 228 print("Waiting for current test(s) to finish."); |
| 201 print("Press Control-C again to terminate immediately."); | 229 print("Press Control-C again to terminate immediately."); |
| 202 }); | 230 }); |
| 203 | 231 |
| 204 reporter.close().then((_) => timer.cancel()); | 232 reporter.close().then((_) => timer.cancel()); |
| 205 }); | 233 }); |
| 206 | 234 |
| 207 return reporter.run().then((success) { | 235 return reporter.run().then((success) { |
| 208 exitCode = success ? 0 : 1; | 236 exitCode = success ? 0 : 1; |
| 209 }).whenComplete(() { | 237 }).whenComplete(() { |
| 210 signalSubscription.cancel(); | 238 signalSubscription.cancel(); |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 256 output = stderr; | 284 output = stderr; |
| 257 } | 285 } |
| 258 | 286 |
| 259 output.write("""$message | 287 output.write("""$message |
| 260 | 288 |
| 261 Usage: pub run test:test [files or directories...] | 289 Usage: pub run test:test [files or directories...] |
| 262 | 290 |
| 263 ${_parser.usage} | 291 ${_parser.usage} |
| 264 """); | 292 """); |
| 265 } | 293 } |
| OLD | NEW |