Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(455)

Side by Side Diff: lib/src/executable.dart

Issue 1083433002: More gracefully handle load errors. (Closed) Base URL: git@github.com:dart-lang/test@master
Patch Set: Code review changes Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « CHANGELOG.md ('k') | lib/src/runner/load_exception_suite.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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';
13 import 'dart:math' as math; 12 import 'dart:math' as math;
14 13
15 import 'package:args/args.dart'; 14 import 'package:args/args.dart';
16 import 'package:stack_trace/stack_trace.dart'; 15 import 'package:stack_trace/stack_trace.dart';
17 import 'package:yaml/yaml.dart'; 16 import 'package:yaml/yaml.dart';
18 17
19 import 'backend/test_platform.dart'; 18 import 'backend/test_platform.dart';
20 import 'runner/reporter/compact.dart'; 19 import 'runner/reporter/compact.dart';
21 import 'runner/load_exception.dart'; 20 import 'runner/load_exception.dart';
21 import 'runner/load_exception_suite.dart';
22 import 'runner/loader.dart'; 22 import 'runner/loader.dart';
23 import 'util/exit_codes.dart' as exit_codes; 23 import 'util/exit_codes.dart' as exit_codes;
24 import 'util/io.dart'; 24 import 'util/io.dart';
25 import 'utils.dart'; 25 import 'utils.dart';
26 26
27 /// The argument parser used to parse the executable arguments. 27 /// The argument parser used to parse the executable arguments.
28 final _parser = new ArgParser(allowTrailingOptions: true); 28 final _parser = new ArgParser(allowTrailingOptions: true);
29 29
30 /// The default number of test suites to run at once. 30 /// The default number of test suites to run at once.
31 /// 31 ///
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
144 try { 144 try {
145 concurrency = int.parse(options["concurrency"]); 145 concurrency = int.parse(options["concurrency"]);
146 } catch (error) { 146 } catch (error) {
147 _printUsage('Couldn\'t parse --concurrency "${options["concurrency"]}":' 147 _printUsage('Couldn\'t parse --concurrency "${options["concurrency"]}":'
148 ' ${error.message}'); 148 ' ${error.message}');
149 exitCode = exit_codes.usage; 149 exitCode = exit_codes.usage;
150 return; 150 return;
151 } 151 }
152 } 152 }
153 153
154 var paths = options.rest;
155 if (paths.isEmpty) {
156 if (!new Directory("test").existsSync()) {
157 _printUsage('No test files were passed and the default "test/" '
158 "directory doesn't exist.");
159 exitCode = exit_codes.data;
160 return;
161 }
162 paths = ["test"];
163 }
164
154 var signalSubscription; 165 var signalSubscription;
155 var closed = false; 166 var closed = false;
156 signalSubscription = _signals.listen((_) { 167 signalSubscription = _signals.listen((_) {
157 signalSubscription.cancel(); 168 signalSubscription.cancel();
158 closed = true; 169 closed = true;
159 loader.close(); 170 loader.close();
160 }); 171 });
161 172
162 new Future.sync(() { 173 mergeStreams(paths.map((path) {
163 var paths = options.rest; 174 if (new Directory(path).existsSync()) return loader.loadDir(path);
164 if (paths.isEmpty) { 175 if (new File(path).existsSync()) return loader.loadFile(path);
165 if (!new Directory("test").existsSync()) { 176 return new Stream.fromFuture(new Future.error(
166 throw new LoadException("test", 177 new LoadException(path, 'Does not exist.'),
167 "No test files were passed and the default directory doesn't " 178 new Trace.current()));
168 "exist."); 179 })).transform(new StreamTransformer.fromHandlers(
169 } 180 handleError: (error, stackTrace, sink) {
170 paths = ["test"]; 181 if (error is! LoadException) {
182 sink.addError(error, stackTrace);
183 } else {
184 sink.add(new LoadExceptionSuite(error));
171 } 185 }
172 186 })).toList().then((suites) {
173 return Future.wait(paths.map((path) {
174 if (new Directory(path).existsSync()) return loader.loadDir(path);
175 if (new File(path).existsSync()) return loader.loadFile(path);
176 throw new LoadException(path, 'Does not exist.');
177 }));
178 }).then((suites) {
179 if (closed) return null; 187 if (closed) return null;
180 suites = flatten(suites); 188 suites = flatten(suites);
181 189
182 var pattern; 190 var pattern;
183 if (options["name"] != null) { 191 if (options["name"] != null) {
184 if (options["plain-name"] != null) { 192 if (options["plain-name"] != null) {
185 _printUsage("--name and --plain-name may not both be passed."); 193 _printUsage("--name and --plain-name may not both be passed.");
186 exitCode = exit_codes.data; 194 exitCode = exit_codes.data;
187 return null; 195 return null;
188 } 196 }
189 pattern = new RegExp(options["name"]); 197 pattern = new RegExp(options["name"]);
190 } else if (options["plain-name"] != null) { 198 } else if (options["plain-name"] != null) {
191 pattern = options["plain-name"]; 199 pattern = options["plain-name"];
192 } 200 }
193 201
194 if (pattern != null) { 202 if (pattern != null) {
195 suites = suites.map((suite) { 203 suites = suites.map((suite) {
204 // Don't ever filter out load errors.
205 if (suite is LoadExceptionSuite) return suite;
196 return suite.change( 206 return suite.change(
197 tests: suite.tests.where((test) => test.name.contains(pattern))); 207 tests: suite.tests.where((test) => test.name.contains(pattern)));
198 }).toList(); 208 }).toList();
199 209
200 if (suites.every((suite) => suite.tests.isEmpty)) { 210 if (suites.every((suite) => suite.tests.isEmpty)) {
201 stderr.write('No tests match '); 211 stderr.write('No tests match ');
202 212
203 if (pattern is RegExp) { 213 if (pattern is RegExp) {
204 stderr.write('regular expression "${pattern.pattern}".'); 214 stderr.writeln('regular expression "${pattern.pattern}".');
205 } else { 215 } else {
206 stderr.writeln('"$pattern".'); 216 stderr.writeln('"$pattern".');
207 } 217 }
208 exitCode = exit_codes.data; 218 exitCode = exit_codes.data;
209 return null; 219 return null;
210 } 220 }
211 } 221 }
212 222
213 var reporter = new CompactReporter(flatten(suites), 223 var reporter = new CompactReporter(flatten(suites),
214 concurrency: concurrency, color: color); 224 concurrency: concurrency, color: color);
(...skipping 17 matching lines...) Expand all
232 reporter.close().then((_) => timer.cancel()); 242 reporter.close().then((_) => timer.cancel());
233 }); 243 });
234 244
235 return reporter.run().then((success) { 245 return reporter.run().then((success) {
236 exitCode = success ? 0 : 1; 246 exitCode = success ? 0 : 1;
237 }).whenComplete(() { 247 }).whenComplete(() {
238 signalSubscription.cancel(); 248 signalSubscription.cancel();
239 return reporter.close(); 249 return reporter.close();
240 }); 250 });
241 }).whenComplete(signalSubscription.cancel).catchError((error, stackTrace) { 251 }).whenComplete(signalSubscription.cancel).catchError((error, stackTrace) {
242 if (error is LoadException) { 252 stderr.writeln(getErrorMessage(error));
243 stderr.writeln(error.toString(color: color)); 253 stderr.writeln(new Trace.from(stackTrace).terse);
244 254 stderr.writeln(
245 // Only print stack traces for load errors that come from the user's 255 "This is an unexpected error. Please file an issue at "
246 if (error.innerError is! IOException && 256 "http://github.com/dart-lang/test\n"
247 error.innerError is! IsolateSpawnException && 257 "with the stack trace and instructions for reproducing the error.");
248 error.innerError is! FormatException && 258 exitCode = exit_codes.software;
249 error.innerError is! String) {
250 stderr.write(terseChain(stackTrace));
251 }
252
253 exitCode = error.innerError is IOException
254 ? exit_codes.io
255 : exit_codes.data;
256 } else {
257 stderr.writeln(getErrorMessage(error));
258 stderr.writeln(new Trace.from(stackTrace).terse);
259 stderr.writeln(
260 "This is an unexpected error. Please file an issue at "
261 "http://github.com/dart-lang/test\n"
262 "with the stack trace and instructions for reproducing the error.");
263 exitCode = exit_codes.software;
264 }
265 }).whenComplete(() { 259 }).whenComplete(() {
266 return loader.close().then((_) { 260 return loader.close().then((_) {
267 // If we're on a Dart version that doesn't support Isolate.kill(), we have 261 // If we're on a Dart version that doesn't support Isolate.kill(), we have
268 // to manually exit so that dangling isolates don't prevent it. 262 // to manually exit so that dangling isolates don't prevent it.
269 if (!supportsIsolateKill) exit(exitCode); 263 if (!supportsIsolateKill) exit(exitCode);
270 }); 264 });
271 }); 265 });
272 } 266 }
273 267
274 /// Print usage information for this command. 268 /// Print usage information for this command.
275 /// 269 ///
276 /// If [error] is passed, it's used in place of the usage message and the whole 270 /// If [error] is passed, it's used in place of the usage message and the whole
277 /// thing is printed to stderr instead of stdout. 271 /// thing is printed to stderr instead of stdout.
278 void _printUsage([String error]) { 272 void _printUsage([String error]) {
279 var output = stdout; 273 var output = stdout;
280 274
281 var message = "Runs tests in this package."; 275 var message = "Runs tests in this package.";
282 if (error != null) { 276 if (error != null) {
283 message = error; 277 message = error;
284 output = stderr; 278 output = stderr;
285 } 279 }
286 280
287 output.write("""$message 281 output.write("""$message
288 282
289 Usage: pub run test:test [files or directories...] 283 Usage: pub run test:test [files or directories...]
290 284
291 ${_parser.usage} 285 ${_parser.usage}
292 """); 286 """);
293 } 287 }
OLDNEW
« no previous file with comments | « CHANGELOG.md ('k') | lib/src/runner/load_exception_suite.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698