| 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 library test.test; | 5 export 'package:test/src/executable.dart'; |
| 6 | |
| 7 import 'dart:async'; | |
| 8 import 'dart:io'; | |
| 9 import 'dart:isolate'; | |
| 10 | |
| 11 import 'package:args/args.dart'; | |
| 12 import 'package:stack_trace/stack_trace.dart'; | |
| 13 | |
| 14 import 'package:test/src/backend/test_platform.dart'; | |
| 15 import 'package:test/src/runner/reporter/compact.dart'; | |
| 16 import 'package:test/src/runner/load_exception.dart'; | |
| 17 import 'package:test/src/runner/loader.dart'; | |
| 18 import 'package:test/src/util/exit_codes.dart' as exit_codes; | |
| 19 import 'package:test/src/util/io.dart'; | |
| 20 import 'package:test/src/utils.dart'; | |
| 21 | |
| 22 /// The argument parser used to parse the executable arguments. | |
| 23 final _parser = new ArgParser(allowTrailingOptions: true); | |
| 24 | |
| 25 void main(List<String> args) { | |
| 26 _parser.addFlag("help", abbr: "h", negatable: false, | |
| 27 help: "Shows this usage information."); | |
| 28 _parser.addOption("package-root", hide: true); | |
| 29 _parser.addOption("name", | |
| 30 abbr: 'n', | |
| 31 help: 'A substring of the name of the test to run.\n' | |
| 32 'Regular expression syntax is supported.'); | |
| 33 _parser.addOption("plain-name", | |
| 34 abbr: 'N', | |
| 35 help: 'A plain-text substring of the name of the test to run.'); | |
| 36 _parser.addOption("platform", | |
| 37 abbr: 'p', | |
| 38 help: 'The platform(s) on which to run the tests.', | |
| 39 allowed: TestPlatform.all.map((platform) => platform.identifier).toList(), | |
| 40 defaultsTo: 'vm', | |
| 41 allowMultiple: true); | |
| 42 _parser.addFlag("color", defaultsTo: null, | |
| 43 help: 'Whether to use terminal colors.\n(auto-detected by default)'); | |
| 44 | |
| 45 var options; | |
| 46 try { | |
| 47 options = _parser.parse(args); | |
| 48 } on FormatException catch (error) { | |
| 49 _printUsage(error.message); | |
| 50 exitCode = exit_codes.usage; | |
| 51 return; | |
| 52 } | |
| 53 | |
| 54 if (options["help"]) { | |
| 55 _printUsage(); | |
| 56 return; | |
| 57 } | |
| 58 | |
| 59 var color = options["color"]; | |
| 60 if (color == null) color = canUseSpecialChars; | |
| 61 | |
| 62 var platforms = options["platform"].map(TestPlatform.find); | |
| 63 var loader = new Loader(platforms, | |
| 64 packageRoot: options["package-root"], color: color); | |
| 65 new Future.sync(() { | |
| 66 var paths = options.rest; | |
| 67 if (paths.isEmpty) { | |
| 68 if (!new Directory("test").existsSync()) { | |
| 69 throw new LoadException("test", | |
| 70 "No test files were passed and the default directory doesn't " | |
| 71 "exist."); | |
| 72 } | |
| 73 paths = ["test"]; | |
| 74 } | |
| 75 | |
| 76 return Future.wait(paths.map((path) { | |
| 77 if (new Directory(path).existsSync()) return loader.loadDir(path); | |
| 78 if (new File(path).existsSync()) return loader.loadFile(path); | |
| 79 throw new LoadException(path, 'Does not exist.'); | |
| 80 })); | |
| 81 }).then((suites) { | |
| 82 suites = flatten(suites); | |
| 83 | |
| 84 var pattern; | |
| 85 if (options["name"] != null) { | |
| 86 if (options["plain-name"] != null) { | |
| 87 _printUsage("--name and --plain-name may not both be passed."); | |
| 88 exitCode = exit_codes.data; | |
| 89 return null; | |
| 90 } | |
| 91 pattern = new RegExp(options["name"]); | |
| 92 } else if (options["plain-name"] != null) { | |
| 93 pattern = options["plain-name"]; | |
| 94 } | |
| 95 | |
| 96 if (pattern != null) { | |
| 97 suites = suites.map((suite) { | |
| 98 return suite.change( | |
| 99 tests: suite.tests.where((test) => test.name.contains(pattern))); | |
| 100 }).toList(); | |
| 101 | |
| 102 if (suites.every((suite) => suite.tests.isEmpty)) { | |
| 103 stderr.write('No tests match '); | |
| 104 | |
| 105 if (pattern is RegExp) { | |
| 106 stderr.write('regular expression "${pattern.pattern}".'); | |
| 107 } else { | |
| 108 stderr.writeln('"$pattern".'); | |
| 109 } | |
| 110 exitCode = exit_codes.data; | |
| 111 return null; | |
| 112 } | |
| 113 } | |
| 114 | |
| 115 var reporter = new CompactReporter(flatten(suites), color: color); | |
| 116 return reporter.run().then((success) { | |
| 117 exitCode = success ? 0 : 1; | |
| 118 }).whenComplete(() => reporter.close()); | |
| 119 }).catchError((error, stackTrace) { | |
| 120 if (error is LoadException) { | |
| 121 stderr.writeln(error.toString(color: color)); | |
| 122 | |
| 123 // Only print stack traces for load errors that come from the user's | |
| 124 if (error.innerError is! IOException && | |
| 125 error.innerError is! IsolateSpawnException && | |
| 126 error.innerError is! FormatException && | |
| 127 error.innerError is! String) { | |
| 128 stderr.write(terseChain(stackTrace)); | |
| 129 } | |
| 130 | |
| 131 exitCode = error.innerError is IOException | |
| 132 ? exit_codes.io | |
| 133 : exit_codes.data; | |
| 134 } else { | |
| 135 stderr.writeln(getErrorMessage(error)); | |
| 136 stderr.writeln(new Trace.from(stackTrace).terse); | |
| 137 stderr.writeln( | |
| 138 "This is an unexpected error. Please file an issue at " | |
| 139 "http://github.com/dart-lang/test\n" | |
| 140 "with the stack trace and instructions for reproducing the error."); | |
| 141 exitCode = exit_codes.software; | |
| 142 } | |
| 143 }).whenComplete(() { | |
| 144 return loader.close().then((_) { | |
| 145 // If we're on a Dart version that doesn't support Isolate.kill(), we have | |
| 146 // to manually exit so that dangling isolates don't prevent it. | |
| 147 if (!supportsIsolateKill) exit(exitCode); | |
| 148 }); | |
| 149 }); | |
| 150 } | |
| 151 | |
| 152 /// Print usage information for this command. | |
| 153 /// | |
| 154 /// If [error] is passed, it's used in place of the usage message and the whole | |
| 155 /// thing is printed to stderr instead of stdout. | |
| 156 void _printUsage([String error]) { | |
| 157 var output = stdout; | |
| 158 | |
| 159 var message = "Runs tests in this package."; | |
| 160 if (error != null) { | |
| 161 message = error; | |
| 162 output = stderr; | |
| 163 } | |
| 164 | |
| 165 output.write("""$message | |
| 166 | |
| 167 Usage: pub run test:test [files or directories...] | |
| 168 | |
| 169 ${_parser.usage} | |
| 170 """); | |
| 171 } | |
| OLD | NEW |