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 |