From 5b387b5cc612f02937fd5c1d88ea431c8bf25c44 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum <nweiz@google.com> Date: Tue, 31 Mar 2015 18:10:43 -0700 Subject: [PATCH] Move the executable's implementation into lib. R=kevmoo@google.com Review URL: https://codereview.chromium.org//1049903002 --- bin/test.dart | 168 +------------------------------------- lib/src/executable.dart | 174 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+), 167 deletions(-) create mode 100644 lib/src/executable.dart diff --git a/bin/test.dart b/bin/test.dart index b2eb10f9..0b40282a 100644 --- a/bin/test.dart +++ b/bin/test.dart @@ -2,170 +2,4 @@ // 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. -library test.test; - -import 'dart:async'; -import 'dart:io'; -import 'dart:isolate'; - -import 'package:args/args.dart'; -import 'package:stack_trace/stack_trace.dart'; - -import 'package:test/src/backend/test_platform.dart'; -import 'package:test/src/runner/reporter/compact.dart'; -import 'package:test/src/runner/load_exception.dart'; -import 'package:test/src/runner/loader.dart'; -import 'package:test/src/util/exit_codes.dart' as exit_codes; -import 'package:test/src/util/io.dart'; -import 'package:test/src/utils.dart'; - -/// The argument parser used to parse the executable arguments. -final _parser = new ArgParser(allowTrailingOptions: true); - -void main(List<String> args) { - _parser.addFlag("help", abbr: "h", negatable: false, - help: "Shows this usage information."); - _parser.addOption("package-root", hide: true); - _parser.addOption("name", - abbr: 'n', - help: 'A substring of the name of the test to run.\n' - 'Regular expression syntax is supported.'); - _parser.addOption("plain-name", - abbr: 'N', - help: 'A plain-text substring of the name of the test to run.'); - _parser.addOption("platform", - abbr: 'p', - help: 'The platform(s) on which to run the tests.', - allowed: TestPlatform.all.map((platform) => platform.identifier).toList(), - defaultsTo: 'vm', - allowMultiple: true); - _parser.addFlag("color", defaultsTo: null, - help: 'Whether to use terminal colors.\n(auto-detected by default)'); - - var options; - try { - options = _parser.parse(args); - } on FormatException catch (error) { - _printUsage(error.message); - exitCode = exit_codes.usage; - return; - } - - if (options["help"]) { - _printUsage(); - return; - } - - var color = options["color"]; - if (color == null) color = canUseSpecialChars; - - var platforms = options["platform"].map(TestPlatform.find); - var loader = new Loader(platforms, - packageRoot: options["package-root"], color: color); - new Future.sync(() { - var paths = options.rest; - if (paths.isEmpty) { - if (!new Directory("test").existsSync()) { - throw new LoadException("test", - "No test files were passed and the default directory doesn't " - "exist."); - } - paths = ["test"]; - } - - return Future.wait(paths.map((path) { - if (new Directory(path).existsSync()) return loader.loadDir(path); - if (new File(path).existsSync()) return loader.loadFile(path); - throw new LoadException(path, 'Does not exist.'); - })); - }).then((suites) { - suites = flatten(suites); - - var pattern; - if (options["name"] != null) { - if (options["plain-name"] != null) { - _printUsage("--name and --plain-name may not both be passed."); - exitCode = exit_codes.data; - return null; - } - pattern = new RegExp(options["name"]); - } else if (options["plain-name"] != null) { - pattern = options["plain-name"]; - } - - if (pattern != null) { - suites = suites.map((suite) { - return suite.change( - tests: suite.tests.where((test) => test.name.contains(pattern))); - }).toList(); - - if (suites.every((suite) => suite.tests.isEmpty)) { - stderr.write('No tests match '); - - if (pattern is RegExp) { - stderr.write('regular expression "${pattern.pattern}".'); - } else { - stderr.writeln('"$pattern".'); - } - exitCode = exit_codes.data; - return null; - } - } - - var reporter = new CompactReporter(flatten(suites), color: color); - return reporter.run().then((success) { - exitCode = success ? 0 : 1; - }).whenComplete(() => reporter.close()); - }).catchError((error, stackTrace) { - if (error is LoadException) { - stderr.writeln(error.toString(color: color)); - - // Only print stack traces for load errors that come from the user's - if (error.innerError is! IOException && - error.innerError is! IsolateSpawnException && - error.innerError is! FormatException && - error.innerError is! String) { - stderr.write(terseChain(stackTrace)); - } - - exitCode = error.innerError is IOException - ? exit_codes.io - : exit_codes.data; - } else { - 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; - } - }).whenComplete(() { - return loader.close().then((_) { - // If we're on a Dart version that doesn't support Isolate.kill(), we have - // to manually exit so that dangling isolates don't prevent it. - if (!supportsIsolateKill) exit(exitCode); - }); - }); -} - -/// 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("""$message - -Usage: pub run test:test [files or directories...] - -${_parser.usage} -"""); -} +export 'package:test/src/executable.dart'; diff --git a/lib/src/executable.dart b/lib/src/executable.dart new file mode 100644 index 00000000..fe403b2a --- /dev/null +++ b/lib/src/executable.dart @@ -0,0 +1,174 @@ +// 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. +library test.executable; + +import 'dart:async'; +import 'dart:io'; +import 'dart:isolate'; + +import 'package:args/args.dart'; +import 'package:stack_trace/stack_trace.dart'; + +import 'backend/test_platform.dart'; +import 'runner/reporter/compact.dart'; +import 'runner/load_exception.dart'; +import 'runner/loader.dart'; +import 'util/exit_codes.dart' as exit_codes; +import 'util/io.dart'; +import 'utils.dart'; + +/// The argument parser used to parse the executable arguments. +final _parser = new ArgParser(allowTrailingOptions: true); + +void main(List<String> args) { + _parser.addFlag("help", abbr: "h", negatable: false, + help: "Shows this usage information."); + _parser.addOption("package-root", hide: true); + _parser.addOption("name", + abbr: 'n', + help: 'A substring of the name of the test to run.\n' + 'Regular expression syntax is supported.'); + _parser.addOption("plain-name", + abbr: 'N', + help: 'A plain-text substring of the name of the test to run.'); + _parser.addOption("platform", + abbr: 'p', + help: 'The platform(s) on which to run the tests.', + allowed: TestPlatform.all.map((platform) => platform.identifier).toList(), + defaultsTo: 'vm', + allowMultiple: true); + _parser.addFlag("color", defaultsTo: null, + help: 'Whether to use terminal colors.\n(auto-detected by default)'); + + var options; + try { + options = _parser.parse(args); + } on FormatException catch (error) { + _printUsage(error.message); + exitCode = exit_codes.usage; + return; + } + + if (options["help"]) { + _printUsage(); + return; + } + + var color = options["color"]; + if (color == null) color = canUseSpecialChars; + + var platforms = options["platform"].map(TestPlatform.find); + var loader = new Loader(platforms, + packageRoot: options["package-root"], color: color); + new Future.sync(() { + var paths = options.rest; + if (paths.isEmpty) { + if (!new Directory("test").existsSync()) { + throw new LoadException("test", + "No test files were passed and the default directory doesn't " + "exist."); + } + paths = ["test"]; + } + + return Future.wait(paths.map((path) { + if (new Directory(path).existsSync()) return loader.loadDir(path); + if (new File(path).existsSync()) return loader.loadFile(path); + throw new LoadException(path, 'Does not exist.'); + })); + }).then((suites) { + suites = flatten(suites); + + var pattern; + if (options["name"] != null) { + if (options["plain-name"] != null) { + _printUsage("--name and --plain-name may not both be passed."); + exitCode = exit_codes.data; + return null; + } + pattern = new RegExp(options["name"]); + } else if (options["plain-name"] != null) { + pattern = options["plain-name"]; + } + + if (pattern != null) { + suites = suites.map((suite) { + return suite.change( + tests: suite.tests.where((test) => test.name.contains(pattern))); + }).toList(); + + if (suites.every((suite) => suite.tests.isEmpty)) { + stderr.write('No tests match '); + + if (pattern is RegExp) { + stderr.write('regular expression "${pattern.pattern}".'); + } else { + stderr.writeln('"$pattern".'); + } + exitCode = exit_codes.data; + return null; + } + } + + var reporter = new CompactReporter(flatten(suites), color: color); + return reporter.run().then((success) { + exitCode = success ? 0 : 1; + }).whenComplete(() => reporter.close()); + }).catchError((error, stackTrace) { + if (error is LoadException) { + stderr.writeln(error.toString(color: color)); + + // Only print stack traces for load errors that come from the user's + if (error.innerError is! IOException && + error.innerError is! IsolateSpawnException && + error.innerError is! FormatException && + error.innerError is! String) { + stderr.write(terseChain(stackTrace)); + } + + exitCode = error.innerError is IOException + ? exit_codes.io + : exit_codes.data; + } else { + 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; + } + }).whenComplete(() { + return loader.close().then((_) { + // If we're on a Dart version that doesn't support Isolate.kill(), we have + // to manually exit so that dangling isolates don't prevent it. + if (!supportsIsolateKill) exit(exitCode); + }); + }); +} + +/// 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("""$message + +Usage: pub run test:test [files or directories...] + +${_parser.usage} +"""); +} -- GitLab