blob: af6cee590f958528dd830acbebc9a00eb2b657f1 [file] [log] [blame]
// Copyright (c) 2016, 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.
library testing.run_tests;
import 'dart:async' show Future;
import 'dart:io' show Directory, File, FileSystemEntity;
import 'dart:io' as io show exitCode;
import 'dart:isolate' show Isolate;
import 'error_handling.dart' show withErrorHandling;
import 'test_root.dart' show TestRoot;
import 'zone_helper.dart' show runGuarded;
import 'log.dart' show enableVerboseOutput, isVerbose, StdoutLogger;
import 'run.dart' show SuiteRunner, runProgram;
class CommandLine {
final Set<String> options;
final List<String> arguments;
CommandLine(this.options, this.arguments);
bool get verbose => options.contains("--verbose") || options.contains("-v");
Set<String> get skip => commaSeparated("--skip=");
Set<String> commaSeparated(String prefix) {
return Set<String>.from(options.expand((String s) {
if (!s.startsWith(prefix)) return const [];
s = s.substring(prefix.length);
return s.split(",");
}));
}
Map<String, String> get environment {
Map<String, String> result = <String, String>{};
for (String option in options) {
if (option.startsWith("-D")) {
int equalIndex = option.indexOf("=");
if (equalIndex != -1) {
String key = option.substring(2, equalIndex);
String value = option.substring(equalIndex + 1);
result[key] = value;
}
}
}
return result;
}
Set<String> get selectedSuites {
return selectors.map((String selector) {
int index = selector.indexOf("/");
return index == -1 ? selector : selector.substring(0, index);
}).toSet();
}
Iterable<String> get selectors => arguments;
Future<Uri?> get configuration async {
const String configPrefix = "--config=";
List<String> configurationPaths = options
.where((String option) => option.startsWith(configPrefix))
.map((String option) => option.substring(configPrefix.length))
.toList();
if (configurationPaths.length > 1) {
fail("Only one --config option is supported");
return null;
}
String configurationPath;
if (configurationPaths.length == 1) {
configurationPath = configurationPaths.single;
File file = File(configurationPath);
if (await file.exists()) {
// If [configurationPath] exists as a file, use the absolute URI. This
// handles absolute paths on Windows.
configurationPath = file.absolute.uri.toString();
}
} else {
configurationPath = "testing.json";
if (!await File(configurationPath).exists()) {
Directory test = Directory("test");
if (await test.exists()) {
List<FileSystemEntity> candidates = await test
.list(recursive: true, followLinks: false)
.where((FileSystemEntity entity) {
return entity is File && entity.uri.path.endsWith("/testing.json");
}).toList();
switch (candidates.length) {
case 0:
fail("Couldn't locate: '$configurationPath'.");
return null;
case 1:
configurationPath = candidates.single.path;
break;
default:
fail("Usage: run_tests.dart [$configPrefix=configuration_file]\n"
"Where configuration_file is one of:\n "
"${candidates.map((file) => file.path).join('\n ')}");
return null;
}
}
}
}
const StdoutLogger()
.logMessage("Reading configuration file '$configurationPath'.");
Uri? configuration =
await Isolate.resolvePackageUri(Uri.base.resolve(configurationPath));
if (configuration == null || !await File.fromUri(configuration).exists()) {
fail("Couldn't locate: '$configurationPath'.");
return null;
}
return configuration;
}
static CommandLine parse(List<String> arguments) {
int index = arguments.indexOf("--");
Set<String> options;
if (index != -1) {
options = Set<String>.from(arguments.getRange(0, index));
arguments = arguments.sublist(index + 1);
} else {
options = arguments.where((argument) => argument.startsWith("-")).toSet();
arguments =
arguments.where((argument) => !argument.startsWith("-")).toList();
}
return CommandLine(options, arguments);
}
}
void fail(String message) {
print(message);
io.exitCode = 1;
}
Future<void> main(List<String> arguments) {
return withErrorHandling(() async {
CommandLine cl = CommandLine.parse(arguments);
if (cl.verbose) {
enableVerboseOutput();
}
Map<String, String> environment = cl.environment;
Uri? configuration = await cl.configuration;
if (configuration == null) return;
if (!isVerbose) {
print("Use --verbose to display more details.");
}
TestRoot root = await TestRoot.fromUri(configuration);
SuiteRunner runner = SuiteRunner(
root.suites, environment, cl.selectors, cl.selectedSuites, cl.skip);
String? program = await runner.generateDartProgram();
bool hasAnalyzerSuites = await runner.analyze(root.packages);
Stopwatch sw = Stopwatch()..start();
if (program == null) {
if (!hasAnalyzerSuites) {
fail("No tests configured.");
}
} else {
await runProgram(program, root.packages);
}
print("Running tests took: ${sw.elapsed}.");
});
}
Future<void> runTests(Map<String, Function> tests) =>
withErrorHandling<void>(() async {
int completed = 0;
for (String name in tests.keys) {
const StdoutLogger()
.logTestStart(completed, 0, tests.length, null, null);
StringBuffer sb = StringBuffer();
try {
await runGuarded(() {
print("Running test $name");
return tests[name]!();
}, printLineOnStdout: sb.writeln);
const StdoutLogger().logMessage(sb);
} catch (e) {
print(sb);
rethrow;
}
const StdoutLogger()
.logTestComplete(++completed, 0, tests.length, null, null);
}
});