blob: 8c82c47d9302b6ebe624cfba4cac8d13fcb9ab95 [file] [log] [blame]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// TODO(nweiz): This is under lib so that it can be used by the unittest dummy
// package. Once that package is no longer being updated, move this back into
// bin.
import 'dart:io';
import 'package:source_span/source_span.dart';
import 'package:stack_trace/stack_trace.dart';
import 'package:yaml/yaml.dart';
import 'runner.dart';
import 'runner/application_exception.dart';
import 'runner/configuration.dart';
import 'runner/version.dart';
import 'util/exit_codes.dart' as exit_codes;
import 'util/io.dart';
import 'utils.dart';
/// A merged stream of all signals that tell the test runner to shut down
/// gracefully.
///
/// Signals will only be captured as long as this has an active subscription.
/// Otherwise, they'll be handled by Dart's default signal handler, which
/// terminates the program immediately.
final _signals = Platform.isWindows
? ProcessSignal.SIGINT.watch()
: mergeStreams([
ProcessSignal.SIGTERM.watch(),
ProcessSignal.SIGINT.watch()
]);
/// Returns whether the current package has a pubspec which uses the
/// `test/pub_serve` transformer.
bool get _usesTransformer {
if (!new File('pubspec.yaml').existsSync()) return false;
var contents = new File('pubspec.yaml').readAsStringSync();
var yaml;
try {
yaml = loadYaml(contents);
} on FormatException {
return false;
}
if (yaml is! Map) return false;
var transformers = yaml['transformers'];
if (transformers == null) return false;
if (transformers is! List) return false;
return transformers.any((transformer) {
if (transformer is String) return transformer == 'test/pub_serve';
if (transformer is! Map) return false;
if (transformer.keys.length != 1) return false;
return transformer.keys.single == 'test/pub_serve';
});
}
main(List<String> args) async {
var configuration;
try {
configuration = new Configuration.parse(args);
} on FormatException catch (error) {
_printUsage(error.message);
exitCode = exit_codes.usage;
return;
}
if (configuration.help) {
_printUsage();
return;
}
if (configuration.version) {
var version = testVersion;
if (version == null) {
stderr.writeln("Couldn't find version number.");
exitCode = exit_codes.data;
} else {
print(version);
}
return;
}
try {
if (new File("dart_test.yaml").existsSync()) {
var fileConfiguration = new Configuration.load("dart_test.yaml");
configuration = fileConfiguration.merge(configuration);
}
} on SourceSpanFormatException catch (error) {
stderr.writeln(error.toString(color: configuration.color));
exitCode = exit_codes.data;
return;
} on FormatException catch (error) {
stderr.writeln(error.message);
exitCode = exit_codes.data;
return;
} on IOException catch (error) {
stderr.writeln(error.toString());
exitCode = exit_codes.noInput;
return;
}
var undefinedPresets =
configuration.chosenPresets
.where((preset) => !configuration.knownPresets.contains(preset))
.toList();
if (undefinedPresets.isNotEmpty) {
_printUsage("Undefined ${pluralize('preset', undefinedPresets.length)} "
"${toSentence(undefinedPresets.map((preset) => '"$preset"'))}.");
exitCode = exit_codes.usage;
return;
}
if (configuration.pubServeUrl != null && !_usesTransformer) {
stderr.write('''
When using --pub-serve, you must include the "test/pub_serve" transformer in
your pubspec:
transformers:
- test/pub_serve:
\$include: test/**_test.dart
''');
exitCode = exit_codes.data;
return;
}
if (!configuration.explicitPaths &&
!new Directory(configuration.paths.single).existsSync()) {
_printUsage('No test files were passed and the default "test/" '
"directory doesn't exist.");
exitCode = exit_codes.data;
return;
}
var runner = new Runner(configuration);
var signalSubscription;
close() async {
if (signalSubscription == null) return;
signalSubscription.cancel();
signalSubscription = null;
stdinLines.cancel(immediate: true);
await runner.close();
}
signalSubscription = _signals.listen((_) => close());
try {
exitCode = (await runner.run()) ? 0 : 1;
} on ApplicationException catch (error) {
stderr.writeln(error.message);
exitCode = exit_codes.data;
} catch (error, stackTrace) {
stderr.writeln(getErrorMessage(error));
stderr.writeln(new Trace.from(stackTrace).terse);
stderr.writeln(
"This is an unexpected error. Please file an issue at "
"http://github.com/dart-lang/test\n"
"with the stack trace and instructions for reproducing the error.");
exitCode = exit_codes.software;
} finally {
await close();
}
}
/// Print usage information for this command.
///
/// If [error] is passed, it's used in place of the usage message and the whole
/// thing is printed to stderr instead of stdout.
void _printUsage([String error]) {
var output = stdout;
var message = "Runs tests in this package.";
if (error != null) {
message = error;
output = stderr;
}
output.write("""${wordWrap(message)}
Usage: pub run test:test [files or directories...]
${Configuration.usage}
""");
}