| Index: tools/testing/dart/test_runner.dart
|
| diff --git a/tools/testing/dart/test_runner.dart b/tools/testing/dart/test_runner.dart
|
| index 1bdf34e2b4d22e29967d886c11ae23140e30d9e3..77a203df378a47c3e074abad3a0ffd25f6c58219 100644
|
| --- a/tools/testing/dart/test_runner.dart
|
| +++ b/tools/testing/dart/test_runner.dart
|
| @@ -11,29 +11,25 @@
|
| */
|
| library test_runner;
|
|
|
| -import "dart:async";
|
| -import "dart:collection" show Queue;
|
| -import "dart:convert" show LineSplitter, UTF8, JSON;
|
| +import 'dart:async';
|
| +import 'dart:collection' show Queue;
|
| +import 'dart:convert' show LineSplitter, UTF8, JSON;
|
| // We need to use the 'io' prefix here, otherwise io.exitCode will shadow
|
| // CommandOutput.exitCode in subclasses of CommandOutput.
|
| -import "dart:io" as io;
|
| -import "dart:math" as math;
|
| -import 'dependency_graph.dart' as dgraph;
|
| -import "browser_controller.dart";
|
| -import "path.dart";
|
| -import "status_file_parser.dart";
|
| -import "test_progress.dart";
|
| -import "test_suite.dart";
|
| -import "utils.dart";
|
| -import 'record_and_replay.dart';
|
| -
|
| -const int CRASHING_BROWSER_EXITCODE = -10;
|
| -const int SLOW_TIMEOUT_MULTIPLIER = 4;
|
| -
|
| -const MESSAGE_CANNOT_OPEN_DISPLAY = 'Gtk-WARNING **: cannot open display';
|
| -const MESSAGE_FAILED_TO_RUN_COMMAND = 'Failed to run command. return code=1';
|
| -
|
| -typedef void TestCaseEvent(TestCase testCase);
|
| +import 'dart:io' as io;
|
| +import 'dart:math' as math;
|
| +
|
| +import 'lib/browser_controller.dart';
|
| +import 'lib/command.dart';
|
| +import 'lib/dependency_graph.dart' as dgraph;
|
| +import 'lib/path.dart';
|
| +import 'lib/record_and_replay.dart';
|
| +import 'lib/test_case.dart';
|
| +import 'lib/test_information.dart';
|
| +import 'lib/test_progress.dart';
|
| +import 'lib/utils.dart';
|
| +import 'test_suite.dart';
|
| +
|
| typedef void ExitCodeEvent(int exitCode);
|
| typedef void EnqueueMoreWork(ProcessQueue queue);
|
|
|
| @@ -43,1651 +39,6 @@ const List<String> EXCLUDED_ENVIRONMENT_VARIABLES =
|
| const ['http_proxy', 'https_proxy', 'no_proxy',
|
| 'HTTP_PROXY', 'HTTPS_PROXY', 'NO_PROXY'];
|
|
|
| -
|
| -/** A command executed as a step in a test case. */
|
| -class Command {
|
| - /** A descriptive name for this command. */
|
| - String displayName;
|
| -
|
| - /** Number of times this command *can* be retried */
|
| - int get maxNumRetries => 2;
|
| -
|
| - /** Reproduction command */
|
| - String get reproductionCommand => null;
|
| -
|
| - // We compute the Command.hashCode lazily and cache it here, since it might
|
| - // be expensive to compute (and hashCode is called often).
|
| - int _cachedHashCode;
|
| -
|
| - Command._(this.displayName);
|
| -
|
| - int get hashCode {
|
| - if (_cachedHashCode == null) {
|
| - var builder = new HashCodeBuilder();
|
| - _buildHashCode(builder);
|
| - _cachedHashCode = builder.value;
|
| - }
|
| - return _cachedHashCode;
|
| - }
|
| -
|
| - operator ==(other) => identical(this, other) ||
|
| - (runtimeType == other.runtimeType && _equal(other));
|
| -
|
| - void _buildHashCode(HashCodeBuilder builder) {
|
| - builder.addJson(displayName);
|
| - }
|
| -
|
| - bool _equal(Command other) =>
|
| - hashCode == other.hashCode &&
|
| - displayName == other.displayName;
|
| -
|
| - String toString() => reproductionCommand;
|
| -
|
| - Future<bool> get outputIsUpToDate => new Future.value(false);
|
| -}
|
| -
|
| -class ProcessCommand extends Command {
|
| - /** Path to the executable of this command. */
|
| - String executable;
|
| -
|
| - /** Command line arguments to the executable. */
|
| - List<String> arguments;
|
| -
|
| - /** Environment for the command */
|
| - Map<String, String> environmentOverrides;
|
| -
|
| - /** Working directory for the command */
|
| - final String workingDirectory;
|
| -
|
| - ProcessCommand._(String displayName, this.executable,
|
| - this.arguments,
|
| - [this.environmentOverrides = null,
|
| - this.workingDirectory = null])
|
| - : super._(displayName) {
|
| - if (io.Platform.operatingSystem == 'windows') {
|
| - // Windows can't handle the first command if it is a .bat file or the like
|
| - // with the slashes going the other direction.
|
| - // NOTE: Issue 1306
|
| - executable = executable.replaceAll('/', '\\');
|
| - }
|
| - }
|
| -
|
| - void _buildHashCode(HashCodeBuilder builder) {
|
| - super._buildHashCode(builder);
|
| - builder.addJson(executable);
|
| - builder.addJson(workingDirectory);
|
| - builder.addJson(arguments);
|
| - builder.addJson(environmentOverrides);
|
| - }
|
| -
|
| - bool _equal(ProcessCommand other) =>
|
| - super._equal(other) &&
|
| - executable == other.executable &&
|
| - deepJsonCompare(arguments, other.arguments) &&
|
| - workingDirectory == other.workingDirectory &&
|
| - deepJsonCompare(environmentOverrides, other.environmentOverrides);
|
| -
|
| - String get reproductionCommand {
|
| - var command = ([executable]..addAll(arguments))
|
| - .map(escapeCommandLineArgument).join(' ');
|
| - if (workingDirectory != null) {
|
| - command = "$command (working directory: $workingDirectory)";
|
| - }
|
| - return command;
|
| - }
|
| -
|
| - Future<bool> get outputIsUpToDate => new Future.value(false);
|
| -}
|
| -
|
| -class CompilationCommand extends ProcessCommand {
|
| - final String _outputFile;
|
| - final bool _neverSkipCompilation;
|
| - final List<Uri> _bootstrapDependencies;
|
| -
|
| - CompilationCommand._(String displayName,
|
| - this._outputFile,
|
| - this._neverSkipCompilation,
|
| - this._bootstrapDependencies,
|
| - String executable,
|
| - List<String> arguments,
|
| - Map<String, String> environmentOverrides)
|
| - : super._(displayName, executable, arguments, environmentOverrides);
|
| -
|
| - Future<bool> get outputIsUpToDate {
|
| - if (_neverSkipCompilation) return new Future.value(false);
|
| -
|
| - Future<List<Uri>> readDepsFile(String path) {
|
| - var file = new io.File(new Path(path).toNativePath());
|
| - if (!file.existsSync()) {
|
| - return new Future.value(null);
|
| - }
|
| - return file.readAsLines().then((List<String> lines) {
|
| - var dependencies = new List<Uri>();
|
| - for (var line in lines) {
|
| - line = line.trim();
|
| - if (line.length > 0) {
|
| - dependencies.add(Uri.parse(line));
|
| - }
|
| - }
|
| - return dependencies;
|
| - });
|
| - }
|
| -
|
| - return readDepsFile("$_outputFile.deps").then((dependencies) {
|
| - if (dependencies != null) {
|
| - dependencies.addAll(_bootstrapDependencies);
|
| - var jsOutputLastModified = TestUtils.lastModifiedCache.getLastModified(
|
| - new Uri(scheme: 'file', path: _outputFile));
|
| - if (jsOutputLastModified != null) {
|
| - for (var dependency in dependencies) {
|
| - var dependencyLastModified =
|
| - TestUtils.lastModifiedCache.getLastModified(dependency);
|
| - if (dependencyLastModified == null ||
|
| - dependencyLastModified.isAfter(jsOutputLastModified)) {
|
| - return false;
|
| - }
|
| - }
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| - });
|
| - }
|
| -
|
| - void _buildHashCode(HashCodeBuilder builder) {
|
| - super._buildHashCode(builder);
|
| - builder.addJson(_outputFile);
|
| - builder.addJson(_neverSkipCompilation);
|
| - builder.addJson(_bootstrapDependencies);
|
| - }
|
| -
|
| - bool _equal(CompilationCommand other) =>
|
| - super._equal(other) &&
|
| - _outputFile == other._outputFile &&
|
| - _neverSkipCompilation == other._neverSkipCompilation &&
|
| - deepJsonCompare(_bootstrapDependencies, other._bootstrapDependencies);
|
| -}
|
| -
|
| -/// This is just a Pair(String, Map) class with hashCode and operator ==
|
| -class AddFlagsKey {
|
| - final String flags;
|
| - final Map env;
|
| - AddFlagsKey(this.flags, this.env);
|
| - // Just use object identity for environment map
|
| - bool operator ==(other) =>
|
| - other is AddFlagsKey && flags == other.flags && env == other.env;
|
| - int get hashCode => flags.hashCode ^ env.hashCode;
|
| -}
|
| -
|
| -class ContentShellCommand extends ProcessCommand {
|
| - ContentShellCommand._(String executable,
|
| - String htmlFile,
|
| - List<String> options,
|
| - List<String> dartFlags,
|
| - Map<String, String> environmentOverrides)
|
| - : super._("content_shell",
|
| - executable,
|
| - _getArguments(options, htmlFile),
|
| - _getEnvironment(environmentOverrides, dartFlags));
|
| -
|
| - // Cache the modified environments in a map from the old environment and
|
| - // the string of Dart flags to the new environment. Avoid creating new
|
| - // environment object for each command object.
|
| - static Map<AddFlagsKey, Map> environments =
|
| - new Map<AddFlagsKey, Map>();
|
| -
|
| - static Map _getEnvironment(Map env, List<String> dartFlags) {
|
| - var needDartFlags = dartFlags != null && dartFlags.length > 0;
|
| - if (needDartFlags) {
|
| - if (env == null) {
|
| - env = const { };
|
| - }
|
| - var flags = dartFlags.join(' ');
|
| - return environments.putIfAbsent(new AddFlagsKey(flags, env),
|
| - () => new Map.from(env)
|
| - ..addAll({'DART_FLAGS': flags, 'DART_FORWARDING_PRINT': '1'}));
|
| - }
|
| - return env;
|
| - }
|
| -
|
| - static List<String> _getArguments(List<String> options, String htmlFile) {
|
| - var arguments = new List.from(options);
|
| - arguments.add(htmlFile);
|
| - return arguments;
|
| - }
|
| -
|
| - int get maxNumRetries => 3;
|
| -}
|
| -
|
| -class BrowserTestCommand extends Command {
|
| - final String browser;
|
| - final String url;
|
| - final Map configuration;
|
| - final bool retry;
|
| -
|
| - BrowserTestCommand._(String _browser,
|
| - this.url,
|
| - this.configuration,
|
| - this.retry)
|
| - : super._(_browser), browser = _browser;
|
| -
|
| - void _buildHashCode(HashCodeBuilder builder) {
|
| - super._buildHashCode(builder);
|
| - builder.addJson(browser);
|
| - builder.addJson(url);
|
| - builder.add(configuration);
|
| - builder.add(retry);
|
| - }
|
| -
|
| - bool _equal(BrowserTestCommand other) =>
|
| - super._equal(other) &&
|
| - browser == other.browser &&
|
| - url == other.url &&
|
| - identical(configuration, other.configuration) &&
|
| - retry == other.retry;
|
| -
|
| - String get reproductionCommand {
|
| - var parts = [TestUtils.dartTestExecutable.toString(),
|
| - 'tools/testing/dart/launch_browser.dart',
|
| - browser,
|
| - url];
|
| - return parts.map(escapeCommandLineArgument).join(' ');
|
| - }
|
| -
|
| - int get maxNumRetries => 4;
|
| -}
|
| -
|
| -class BrowserHtmlTestCommand extends BrowserTestCommand {
|
| - List<String> expectedMessages;
|
| - BrowserHtmlTestCommand._(String browser,
|
| - String url,
|
| - Map configuration,
|
| - this.expectedMessages,
|
| - bool retry)
|
| - : super._(browser, url, configuration, retry);
|
| -
|
| - void _buildHashCode(HashCodeBuilder builder) {
|
| - super._buildHashCode(builder);
|
| - builder.addJson(expectedMessages);
|
| - }
|
| -
|
| - bool _equal(BrowserHtmlTestCommand other) =>
|
| - super._equal(other) &&
|
| - identical(expectedMessages, other.expectedMessages);
|
| -}
|
| -
|
| -class AnalysisCommand extends ProcessCommand {
|
| - final String flavor;
|
| -
|
| - AnalysisCommand._(this.flavor,
|
| - String displayName,
|
| - String executable,
|
| - List<String> arguments,
|
| - Map<String, String> environmentOverrides)
|
| - : super._(displayName, executable, arguments, environmentOverrides);
|
| -
|
| - void _buildHashCode(HashCodeBuilder builder) {
|
| - super._buildHashCode(builder);
|
| - builder.addJson(flavor);
|
| - }
|
| -
|
| - bool _equal(AnalysisCommand other) =>
|
| - super._equal(other) &&
|
| - flavor == other.flavor;
|
| -}
|
| -
|
| -class VmCommand extends ProcessCommand {
|
| - VmCommand._(String executable,
|
| - List<String> arguments,
|
| - Map<String,String> environmentOverrides)
|
| - : super._("vm", executable, arguments, environmentOverrides);
|
| -}
|
| -
|
| -class JSCommandlineCommand extends ProcessCommand {
|
| - JSCommandlineCommand._(String displayName,
|
| - String executable,
|
| - List<String> arguments,
|
| - [Map<String, String> environmentOverrides = null])
|
| - : super._(displayName,
|
| - executable,
|
| - arguments,
|
| - environmentOverrides);
|
| -}
|
| -
|
| -class PubCommand extends ProcessCommand {
|
| - final String command;
|
| -
|
| - PubCommand._(String pubCommand,
|
| - String pubExecutable,
|
| - String pubspecYamlDirectory,
|
| - String pubCacheDirectory)
|
| - : super._('pub_$pubCommand',
|
| - new io.File(pubExecutable).absolute.path,
|
| - [pubCommand],
|
| - {'PUB_CACHE' : pubCacheDirectory},
|
| - pubspecYamlDirectory), command = pubCommand;
|
| -
|
| - void _buildHashCode(HashCodeBuilder builder) {
|
| - super._buildHashCode(builder);
|
| - builder.addJson(command);
|
| - }
|
| -
|
| - bool _equal(PubCommand other) =>
|
| - super._equal(other) &&
|
| - command == other.command;
|
| -}
|
| -
|
| -/* [ScriptCommand]s are executed by dart code. */
|
| -abstract class ScriptCommand extends Command {
|
| - ScriptCommand._(String displayName) : super._(displayName);
|
| -
|
| - Future<ScriptCommandOutputImpl> run();
|
| -}
|
| -
|
| -class CleanDirectoryCopyCommand extends ScriptCommand {
|
| - final String _sourceDirectory;
|
| - final String _destinationDirectory;
|
| -
|
| - CleanDirectoryCopyCommand._(this._sourceDirectory, this._destinationDirectory)
|
| - : super._('dir_copy');
|
| -
|
| - String get reproductionCommand =>
|
| - "Copying '$_sourceDirectory' to '$_destinationDirectory'.";
|
| -
|
| - Future<ScriptCommandOutputImpl> run() {
|
| - var watch = new Stopwatch()..start();
|
| -
|
| - var destination = new io.Directory(_destinationDirectory);
|
| -
|
| - return destination.exists().then((bool exists) {
|
| - var cleanDirectoryFuture;
|
| - if (exists) {
|
| - cleanDirectoryFuture = TestUtils.deleteDirectory(_destinationDirectory);
|
| - } else {
|
| - cleanDirectoryFuture = new Future.value(null);
|
| - }
|
| - return cleanDirectoryFuture.then((_) {
|
| - return TestUtils.copyDirectory(_sourceDirectory, _destinationDirectory);
|
| - });
|
| - }).then((_) {
|
| - return new ScriptCommandOutputImpl(
|
| - this, Expectation.PASS, "", watch.elapsed);
|
| - }).catchError((error) {
|
| - return new ScriptCommandOutputImpl(
|
| - this, Expectation.FAIL, "An error occured: $error.", watch.elapsed);
|
| - });
|
| - }
|
| -
|
| - void _buildHashCode(HashCodeBuilder builder) {
|
| - super._buildHashCode(builder);
|
| - builder.addJson(_sourceDirectory);
|
| - builder.addJson(_destinationDirectory);
|
| - }
|
| -
|
| - bool _equal(CleanDirectoryCopyCommand other) =>
|
| - super._equal(other) &&
|
| - _sourceDirectory == other._sourceDirectory &&
|
| - _destinationDirectory == other._destinationDirectory;
|
| -}
|
| -
|
| -class ModifyPubspecYamlCommand extends ScriptCommand {
|
| - String _pubspecYamlFile;
|
| - String _destinationFile;
|
| - Map<String, Map> _dependencyOverrides;
|
| -
|
| - ModifyPubspecYamlCommand._(this._pubspecYamlFile,
|
| - this._destinationFile,
|
| - this._dependencyOverrides)
|
| - : super._("modify_pubspec") {
|
| - assert(_pubspecYamlFile.endsWith("pubspec.yaml"));
|
| - assert(_destinationFile.endsWith("pubspec.yaml"));
|
| - }
|
| -
|
| - String get reproductionCommand =>
|
| - "Adding necessary dependency overrides to '$_pubspecYamlFile' "
|
| - "(destination = $_destinationFile).";
|
| -
|
| - Future<ScriptCommandOutputImpl> run() {
|
| - var watch = new Stopwatch()..start();
|
| -
|
| - var pubspecLockFile =
|
| - _destinationFile.substring(0, _destinationFile.length - ".yaml".length)
|
| - + ".lock";
|
| -
|
| - var file = new io.File(_pubspecYamlFile);
|
| - var destinationFile = new io.File(_destinationFile);
|
| - var lockfile = new io.File(pubspecLockFile);
|
| - return file.readAsString().then((String yamlString) {
|
| - var dependencyOverrideSection = new StringBuffer();
|
| - if (_dependencyOverrides.isNotEmpty) {
|
| - dependencyOverrideSection.write(
|
| - "\n"
|
| - "# This section was autogenerated by test.py!\n"
|
| - "dependency_overrides:\n");
|
| - _dependencyOverrides.forEach((String packageName, Map override) {
|
| - dependencyOverrideSection.write(" $packageName:\n");
|
| - override.forEach((overrideKey, overrideValue) {
|
| - dependencyOverrideSection.write(
|
| - " $overrideKey: $overrideValue\n");
|
| - });
|
| - });
|
| - }
|
| - var modifiedYamlString = "$yamlString\n$dependencyOverrideSection";
|
| - return destinationFile.writeAsString(modifiedYamlString).then((_) {
|
| - lockfile.exists().then((bool lockfileExists) {
|
| - if (lockfileExists) {
|
| - return lockfile.delete();
|
| - }
|
| - });
|
| - });
|
| - }).then((_) {
|
| - return new ScriptCommandOutputImpl(
|
| - this, Expectation.PASS, "", watch.elapsed);
|
| - }).catchError((error) {
|
| - return new ScriptCommandOutputImpl(
|
| - this, Expectation.FAIL, "An error occured: $error.", watch.elapsed);
|
| - });
|
| - }
|
| -
|
| - void _buildHashCode(HashCodeBuilder builder) {
|
| - super._buildHashCode(builder);
|
| - builder.addJson(_pubspecYamlFile);
|
| - builder.addJson(_destinationFile);
|
| - builder.addJson(_dependencyOverrides);
|
| - }
|
| -
|
| - bool _equal(ModifyPubspecYamlCommand other) =>
|
| - super._equal(other) &&
|
| - _pubspecYamlFile == other._pubspecYamlFile &&
|
| - _destinationFile == other._destinationFile &&
|
| - deepJsonCompare(_dependencyOverrides, other._dependencyOverrides);
|
| -}
|
| -
|
| -/*
|
| - * [MakeSymlinkCommand] makes a symbolic link to another directory.
|
| - */
|
| -class MakeSymlinkCommand extends ScriptCommand {
|
| - String _link;
|
| - String _target;
|
| -
|
| - MakeSymlinkCommand._(this._link, this._target) : super._('make_symlink');
|
| -
|
| - String get reproductionCommand =>
|
| - "Make symbolic link '$_link' (target: $_target)'.";
|
| -
|
| - Future<ScriptCommandOutputImpl> run() {
|
| - var watch = new Stopwatch()..start();
|
| - var targetFile = new io.Directory(_target);
|
| - return targetFile.exists().then((bool targetExists) {
|
| - if (!targetExists) {
|
| - throw new Exception("Target '$_target' does not exist");
|
| - }
|
| - var link = new io.Link(_link);
|
| -
|
| - return link.exists()
|
| - .then((bool exists) { if (exists) return link.delete(); })
|
| - .then((_) => link.create(_target));
|
| - }).then((_) {
|
| - return new ScriptCommandOutputImpl(
|
| - this, Expectation.PASS, "", watch.elapsed);
|
| - }).catchError((error) {
|
| - return new ScriptCommandOutputImpl(
|
| - this, Expectation.FAIL, "An error occured: $error.", watch.elapsed);
|
| - });
|
| - }
|
| -
|
| - void _buildHashCode(HashCodeBuilder builder) {
|
| - super._buildHashCode(builder);
|
| - builder.addJson(_link);
|
| - builder.addJson(_target);
|
| - }
|
| -
|
| - bool _equal(MakeSymlinkCommand other) =>
|
| - super._equal(other) &&
|
| - _link == other._link &&
|
| - _target == other._target;
|
| -}
|
| -
|
| -class CommandBuilder {
|
| - static final CommandBuilder instance = new CommandBuilder._();
|
| -
|
| - bool _cleared = false;
|
| - final _cachedCommands = new Map<Command, Command>();
|
| -
|
| - CommandBuilder._();
|
| -
|
| - void clearCommandCache() {
|
| - _cachedCommands.clear();
|
| - _cleared = true;
|
| - }
|
| -
|
| - ContentShellCommand getContentShellCommand(String executable,
|
| - String htmlFile,
|
| - List<String> options,
|
| - List<String> dartFlags,
|
| - Map<String, String> environment) {
|
| - ContentShellCommand command = new ContentShellCommand._(
|
| - executable, htmlFile, options, dartFlags, environment);
|
| - return _getUniqueCommand(command);
|
| - }
|
| -
|
| - BrowserTestCommand getBrowserTestCommand(String browser,
|
| - String url,
|
| - Map configuration,
|
| - bool retry) {
|
| - var command = new BrowserTestCommand._(browser, url, configuration, retry);
|
| - return _getUniqueCommand(command);
|
| - }
|
| -
|
| - BrowserHtmlTestCommand getBrowserHtmlTestCommand(String browser,
|
| - String url,
|
| - Map configuration,
|
| - List<String> expectedMessages,
|
| - bool retry) {
|
| - var command = new BrowserHtmlTestCommand._(
|
| - browser, url, configuration, expectedMessages, retry);
|
| - return _getUniqueCommand(command);
|
| - }
|
| -
|
| - CompilationCommand getCompilationCommand(String displayName,
|
| - outputFile,
|
| - neverSkipCompilation,
|
| - List<Uri> bootstrapDependencies,
|
| - String executable,
|
| - List<String> arguments,
|
| - Map<String, String> environment) {
|
| - var command =
|
| - new CompilationCommand._(
|
| - displayName, outputFile, neverSkipCompilation,
|
| - bootstrapDependencies, executable, arguments, environment);
|
| - return _getUniqueCommand(command);
|
| - }
|
| -
|
| - AnalysisCommand getAnalysisCommand(
|
| - String displayName, executable, arguments, environmentOverrides,
|
| - {String flavor: 'dartanalyzer'}) {
|
| - var command = new AnalysisCommand._(
|
| - flavor, displayName, executable, arguments, environmentOverrides);
|
| - return _getUniqueCommand(command);
|
| - }
|
| -
|
| - VmCommand getVmCommand(String executable,
|
| - List<String> arguments,
|
| - Map<String, String> environmentOverrides) {
|
| - var command = new VmCommand._(executable, arguments, environmentOverrides);
|
| - return _getUniqueCommand(command);
|
| - }
|
| -
|
| - Command getJSCommandlineCommand(String displayName, executable, arguments,
|
| - [environment = null]) {
|
| - var command = new JSCommandlineCommand._(displayName, executable, arguments,
|
| - environment);
|
| - return _getUniqueCommand(command);
|
| - }
|
| -
|
| - Command getProcessCommand(String displayName, executable, arguments,
|
| - [environment = null, workingDirectory = null]) {
|
| - var command = new ProcessCommand._(displayName, executable, arguments,
|
| - environment, workingDirectory);
|
| - return _getUniqueCommand(command);
|
| - }
|
| -
|
| - Command getCopyCommand(String sourceDirectory, String destinationDirectory) {
|
| - var command = new CleanDirectoryCopyCommand._(sourceDirectory,
|
| - destinationDirectory);
|
| - return _getUniqueCommand(command);
|
| - }
|
| -
|
| - Command getPubCommand(String pubCommand,
|
| - String pubExecutable,
|
| - String pubspecYamlDirectory,
|
| - String pubCacheDirectory) {
|
| - var command = new PubCommand._(pubCommand,
|
| - pubExecutable,
|
| - pubspecYamlDirectory,
|
| - pubCacheDirectory);
|
| - return _getUniqueCommand(command);
|
| - }
|
| -
|
| - Command getMakeSymlinkCommand(String link, String target) {
|
| - return _getUniqueCommand(new MakeSymlinkCommand._(link, target));
|
| - }
|
| -
|
| - Command getModifyPubspecCommand(String pubspecYamlFile, Map depsOverrides,
|
| - {String destinationFile: null}) {
|
| - if (destinationFile == null) destinationFile = pubspecYamlFile;
|
| - return _getUniqueCommand(new ModifyPubspecYamlCommand._(
|
| - pubspecYamlFile, destinationFile, depsOverrides));
|
| - }
|
| -
|
| - Command _getUniqueCommand(Command command) {
|
| - // All Command classes implement hashCode and operator==.
|
| - // We check if this command has already been built.
|
| - // If so, we return the cached one. Otherwise we
|
| - // store the one given as [command] argument.
|
| - if (_cleared) {
|
| - throw new Exception(
|
| - "CommandBuilder.get[type]Command called after cache cleared");
|
| - }
|
| - var cachedCommand = _cachedCommands[command];
|
| - if (cachedCommand != null) {
|
| - return cachedCommand;
|
| - }
|
| - _cachedCommands[command] = command;
|
| - return command;
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * TestCase contains all the information needed to run a test and evaluate
|
| - * its output. Running a test involves starting a separate process, with
|
| - * the executable and arguments given by the TestCase, and recording its
|
| - * stdout and stderr output streams, and its exit code. TestCase only
|
| - * contains static information about the test; actually running the test is
|
| - * performed by [ProcessQueue] using a [RunningProcess] object.
|
| - *
|
| - * The output information is stored in a [CommandOutput] instance contained
|
| - * in TestCase.commandOutputs. The last CommandOutput instance is responsible
|
| - * for evaluating if the test has passed, failed, crashed, or timed out, and the
|
| - * TestCase has information about what the expected result of the test should
|
| - * be.
|
| - *
|
| - * The TestCase has a callback function, [completedHandler], that is run when
|
| - * the test is completed.
|
| - */
|
| -class TestCase extends UniqueObject {
|
| - // Flags set in _expectations from the optional argument info.
|
| - static final int IS_NEGATIVE = 1 << 0;
|
| - static final int HAS_RUNTIME_ERROR = 1 << 1;
|
| - static final int HAS_STATIC_WARNING = 1 << 2;
|
| - static final int IS_NEGATIVE_IF_CHECKED = 1 << 3;
|
| - static final int HAS_COMPILE_ERROR = 1 << 4;
|
| - static final int HAS_COMPILE_ERROR_IF_CHECKED = 1 << 5;
|
| - static final int EXPECT_COMPILE_ERROR = 1 << 6;
|
| - /**
|
| - * A list of commands to execute. Most test cases have a single command.
|
| - * Dart2js tests have two commands, one to compile the source and another
|
| - * to execute it. Some isolate tests might even have three, if they require
|
| - * compiling multiple sources that are run in isolation.
|
| - */
|
| - List<Command> commands;
|
| - Map<Command, CommandOutput> commandOutputs = new Map<Command,CommandOutput>();
|
| -
|
| - Map configuration;
|
| - String displayName;
|
| - int _expectations = 0;
|
| - int hash = 0;
|
| - Set<Expectation> expectedOutcomes;
|
| -
|
| - TestCase(this.displayName,
|
| - this.commands,
|
| - this.configuration,
|
| - this.expectedOutcomes,
|
| - {isNegative: false,
|
| - TestInformation info: null}) {
|
| - if (isNegative || displayName.contains("negative_test")) {
|
| - _expectations |= IS_NEGATIVE;
|
| - }
|
| - if (info != null) {
|
| - _setExpectations(info);
|
| - hash = info.originTestPath.relativeTo(TestUtils.dartDir)
|
| - .toString().hashCode;
|
| - }
|
| - }
|
| -
|
| - void _setExpectations(TestInformation info) {
|
| - // We don't want to keep the entire (large) TestInformation structure,
|
| - // so we copy the needed bools into flags set in a single integer.
|
| - if (info.hasRuntimeError) _expectations |= HAS_RUNTIME_ERROR;
|
| - if (info.hasStaticWarning) _expectations |= HAS_STATIC_WARNING;
|
| - if (info.isNegativeIfChecked) _expectations |= IS_NEGATIVE_IF_CHECKED;
|
| - if (info.hasCompileError) _expectations |= HAS_COMPILE_ERROR;
|
| - if (info.hasCompileErrorIfChecked) {
|
| - _expectations |= HAS_COMPILE_ERROR_IF_CHECKED;
|
| - }
|
| - if (info.hasCompileError ||
|
| - (configuration['checked'] && info.hasCompileErrorIfChecked)) {
|
| - _expectations |= EXPECT_COMPILE_ERROR;
|
| - }
|
| - }
|
| -
|
| - bool get isNegative => _expectations & IS_NEGATIVE != 0;
|
| - bool get hasRuntimeError => _expectations & HAS_RUNTIME_ERROR != 0;
|
| - bool get hasStaticWarning => _expectations & HAS_STATIC_WARNING != 0;
|
| - bool get isNegativeIfChecked => _expectations & IS_NEGATIVE_IF_CHECKED != 0;
|
| - bool get hasCompileError => _expectations & HAS_COMPILE_ERROR != 0;
|
| - bool get hasCompileErrorIfChecked =>
|
| - _expectations & HAS_COMPILE_ERROR_IF_CHECKED != 0;
|
| - bool get expectCompileError => _expectations & EXPECT_COMPILE_ERROR != 0;
|
| -
|
| - bool get unexpectedOutput {
|
| - var outcome = lastCommandOutput.result(this);
|
| - return !expectedOutcomes.any((expectation) {
|
| - return outcome.canBeOutcomeOf(expectation);
|
| - });
|
| - }
|
| -
|
| - Expectation get result => lastCommandOutput.result(this);
|
| -
|
| - CommandOutput get lastCommandOutput {
|
| - if (commandOutputs.length == 0) {
|
| - throw new Exception("CommandOutputs is empty, maybe no command was run? ("
|
| - "displayName: '$displayName', "
|
| - "configurationString: '$configurationString')");
|
| - }
|
| - return commandOutputs[commands[commandOutputs.length - 1]];
|
| - }
|
| -
|
| - Command get lastCommandExecuted {
|
| - if (commandOutputs.length == 0) {
|
| - throw new Exception("CommandOutputs is empty, maybe no command was run? ("
|
| - "displayName: '$displayName', "
|
| - "configurationString: '$configurationString')");
|
| - }
|
| - return commands[commandOutputs.length - 1];
|
| - }
|
| -
|
| - int get timeout {
|
| - if (expectedOutcomes.contains(Expectation.SLOW)) {
|
| - return configuration['timeout'] * SLOW_TIMEOUT_MULTIPLIER;
|
| - } else {
|
| - return configuration['timeout'];
|
| - }
|
| - }
|
| -
|
| - String get configurationString {
|
| - final compiler = configuration['compiler'];
|
| - final runtime = configuration['runtime'];
|
| - final mode = configuration['mode'];
|
| - final arch = configuration['arch'];
|
| - final checked = configuration['checked'] ? '-checked' : '';
|
| - return "$compiler-$runtime$checked ${mode}_$arch";
|
| - }
|
| -
|
| - List<String> get batchTestArguments {
|
| - assert(commands.last is ProcessCommand);
|
| - return (commands.last as ProcessCommand).arguments;
|
| - }
|
| -
|
| - bool get isFlaky {
|
| - if (expectedOutcomes.contains(Expectation.SKIP) ||
|
| - expectedOutcomes.contains(Expectation.SKIP_BY_DESIGN)) {
|
| - return false;
|
| - }
|
| -
|
| - return expectedOutcomes
|
| - .where((expectation) => !expectation.isMetaExpectation).length > 1;
|
| - }
|
| -
|
| - bool get isFinished {
|
| - return commandOutputs.length > 0 &&
|
| - (!lastCommandOutput.successful ||
|
| - commands.length == commandOutputs.length);
|
| - }
|
| -}
|
| -
|
| -
|
| -/**
|
| - * BrowserTestCase has an extra compilation command that is run in a separate
|
| - * process, before the regular test is run as in the base class [TestCase].
|
| - * If the compilation command fails, then the rest of the test is not run.
|
| - */
|
| -class BrowserTestCase extends TestCase {
|
| -
|
| - BrowserTestCase(displayName, commands, configuration,
|
| - expectedOutcomes, info, isNegative, this._testingUrl)
|
| - : super(displayName, commands, configuration,
|
| - expectedOutcomes, isNegative: isNegative, info: info);
|
| -
|
| - String _testingUrl;
|
| -
|
| - String get testingUrl => _testingUrl;
|
| -}
|
| -
|
| -class UnittestSuiteMessagesMixin {
|
| - bool _isAsyncTest(String testOutput) {
|
| - return testOutput.contains("unittest-suite-wait-for-done");
|
| - }
|
| -
|
| - bool _isAsyncTestSuccessful(String testOutput) {
|
| - return testOutput.contains("unittest-suite-success");
|
| - }
|
| -
|
| - Expectation _negateOutcomeIfIncompleteAsyncTest(Expectation outcome,
|
| - String testOutput) {
|
| - // If this is an asynchronous test and the asynchronous operation didn't
|
| - // complete successfully, it's outcome is Expectation.FAIL.
|
| - // TODO: maybe we should introduce a AsyncIncomplete marker or so
|
| - if (outcome == Expectation.PASS) {
|
| - if (_isAsyncTest(testOutput) &&
|
| - !_isAsyncTestSuccessful(testOutput)) {
|
| - return Expectation.FAIL;
|
| - }
|
| - }
|
| - return outcome;
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * CommandOutput records the output of a completed command: the process's exit
|
| - * code, the standard output and standard error, whether the process timed out,
|
| - * and the time the process took to run. It also contains a pointer to the
|
| - * [TestCase] this is the output of.
|
| - */
|
| -abstract class CommandOutput {
|
| - Command get command;
|
| -
|
| - Expectation result(TestCase testCase);
|
| -
|
| - bool get hasCrashed;
|
| -
|
| - bool get hasTimedOut;
|
| -
|
| - bool didFail(testcase);
|
| -
|
| - bool hasFailed(TestCase testCase);
|
| -
|
| - bool get canRunDependendCommands;
|
| -
|
| - bool get successful; // otherwise we might to retry running
|
| -
|
| - Duration get time;
|
| -
|
| - int get exitCode;
|
| -
|
| - int get pid;
|
| -
|
| - List<int> get stdout;
|
| -
|
| - List<int> get stderr;
|
| -
|
| - List<String> get diagnostics;
|
| -
|
| - bool get compilationSkipped;
|
| -}
|
| -
|
| -class CommandOutputImpl extends UniqueObject implements CommandOutput {
|
| - Command command;
|
| - int exitCode;
|
| -
|
| - bool timedOut;
|
| - List<int> stdout;
|
| - List<int> stderr;
|
| - Duration time;
|
| - List<String> diagnostics;
|
| - bool compilationSkipped;
|
| - int pid;
|
| -
|
| - /**
|
| - * A flag to indicate we have already printed a warning about ignoring the VM
|
| - * crash, to limit the amount of output produced per test.
|
| - */
|
| - bool alreadyPrintedWarning = false;
|
| -
|
| - // TODO(kustermann): Remove testCase from this class.
|
| - CommandOutputImpl(Command this.command,
|
| - int this.exitCode,
|
| - bool this.timedOut,
|
| - List<int> this.stdout,
|
| - List<int> this.stderr,
|
| - Duration this.time,
|
| - bool this.compilationSkipped,
|
| - int this.pid) {
|
| - diagnostics = [];
|
| - }
|
| -
|
| - Expectation result(TestCase testCase) {
|
| - if (hasCrashed) return Expectation.CRASH;
|
| - if (hasTimedOut) return Expectation.TIMEOUT;
|
| - return hasFailed(testCase) ? Expectation.FAIL : Expectation.PASS;
|
| - }
|
| -
|
| - bool get hasCrashed {
|
| - // The Java dartc runner and dart2js exits with code 253 in case
|
| - // of unhandled exceptions.
|
| - if (exitCode == 253) return true;
|
| - if (io.Platform.operatingSystem == 'windows') {
|
| - // The VM uses std::abort to terminate on asserts.
|
| - // std::abort terminates with exit code 3 on Windows.
|
| - if (exitCode == 3 || exitCode == CRASHING_BROWSER_EXITCODE) {
|
| - return !timedOut;
|
| - }
|
| - // If a program receives an uncaught system exception, the program
|
| - // terminates with the exception code as exit code.
|
| - // The 0x3FFFFF00 mask here tries to determine if an exception indicates
|
| - // a crash of the program.
|
| - // System exception codes can be found in 'winnt.h', for example
|
| - // "#define STATUS_ACCESS_VIOLATION ((DWORD) 0xC0000005)"
|
| - return (!timedOut && (exitCode < 0) && ((0x3FFFFF00 & exitCode) == 0));
|
| - }
|
| - return !timedOut && ((exitCode < 0));
|
| - }
|
| -
|
| - bool get hasTimedOut => timedOut;
|
| -
|
| - bool didFail(TestCase testCase) {
|
| - return (exitCode != 0 && !hasCrashed);
|
| - }
|
| -
|
| - bool get canRunDependendCommands {
|
| - // FIXME(kustermann): We may need to change this
|
| - return !hasTimedOut && exitCode == 0;
|
| - }
|
| -
|
| - bool get successful {
|
| - // FIXME(kustermann): We may need to change this
|
| - return !hasTimedOut && exitCode == 0;
|
| - }
|
| -
|
| - // Reverse result of a negative test.
|
| - bool hasFailed(TestCase testCase) {
|
| - return testCase.isNegative ? !didFail(testCase) : didFail(testCase);
|
| - }
|
| -
|
| - Expectation _negateOutcomeIfNegativeTest(Expectation outcome,
|
| - bool isNegative) {
|
| - if (!isNegative) return outcome;
|
| -
|
| - if (outcome.canBeOutcomeOf(Expectation.FAIL)) {
|
| - return Expectation.PASS;
|
| - }
|
| - return Expectation.FAIL;
|
| - }
|
| -}
|
| -
|
| -class BrowserCommandOutputImpl extends CommandOutputImpl {
|
| - // Although tests are reported as passing, content shell sometimes exits with
|
| - // a nonzero exitcode which makes our dartium builders extremely falky.
|
| - // See: http://dartbug.com/15139.
|
| - static int WHITELISTED_CONTENTSHELL_EXITCODE = -1073740022;
|
| - static bool isWindows = io.Platform.operatingSystem == 'windows';
|
| -
|
| - bool _failedBecauseOfMissingXDisplay;
|
| -
|
| - BrowserCommandOutputImpl(
|
| - command,
|
| - exitCode,
|
| - timedOut,
|
| - stdout,
|
| - stderr,
|
| - time,
|
| - compilationSkipped) :
|
| - super(command,
|
| - exitCode,
|
| - timedOut,
|
| - stdout,
|
| - stderr,
|
| - time,
|
| - compilationSkipped,
|
| - 0) {
|
| - _failedBecauseOfMissingXDisplay = _didFailBecauseOfMissingXDisplay();
|
| - if (_failedBecauseOfMissingXDisplay) {
|
| - DebugLogger.warning("Warning: Test failure because of missing XDisplay");
|
| - // If we get the X server error, or DRT crashes with a core dump, retry
|
| - // the test.
|
| - }
|
| - }
|
| -
|
| - Expectation result(TestCase testCase) {
|
| - // Handle crashes and timeouts first
|
| - if (hasCrashed) return Expectation.CRASH;
|
| - if (hasTimedOut) return Expectation.TIMEOUT;
|
| -
|
| - var outcome = _getOutcome();
|
| -
|
| - if (testCase.hasRuntimeError) {
|
| - if (!outcome.canBeOutcomeOf(Expectation.RUNTIME_ERROR)) {
|
| - return Expectation.MISSING_RUNTIME_ERROR;
|
| - }
|
| - }
|
| - if (testCase.isNegative) {
|
| - if (outcome.canBeOutcomeOf(Expectation.FAIL)) return Expectation.PASS;
|
| - return Expectation.FAIL;
|
| - }
|
| - return outcome;
|
| - }
|
| -
|
| - bool get successful => canRunDependendCommands;
|
| -
|
| - bool get canRunDependendCommands {
|
| - // We cannot rely on the exit code of content_shell as a method to determine
|
| - // if we were successful or not.
|
| - return super.canRunDependendCommands && !didFail(null);
|
| - }
|
| -
|
| - bool get hasCrashed {
|
| - return super.hasCrashed || _rendererCrashed;
|
| - }
|
| -
|
| - Expectation _getOutcome() {
|
| - if (_failedBecauseOfMissingXDisplay) {
|
| - return Expectation.FAIL;
|
| - }
|
| -
|
| - if (_browserTestFailure) {
|
| - return Expectation.RUNTIME_ERROR;
|
| - }
|
| - return Expectation.PASS;
|
| - }
|
| -
|
| - bool _didFailBecauseOfMissingXDisplay() {
|
| - // Browser case:
|
| - // If the browser test failed, it may have been because content shell
|
| - // and the virtual framebuffer X server didn't hook up, or it crashed with
|
| - // a core dump. Sometimes content shell crashes after it has set the stdout
|
| - // to PASS, so we have to do this check first.
|
| - var stderrLines = decodeUtf8(super.stderr).split("\n");
|
| - for (String line in stderrLines) {
|
| - // TODO(kustermann,ricow): Issue: 7564
|
| - // This seems to happen quite frequently, we need to figure out why.
|
| - if (line.contains(MESSAGE_CANNOT_OPEN_DISPLAY) ||
|
| - line.contains(MESSAGE_FAILED_TO_RUN_COMMAND)) {
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - bool get _rendererCrashed =>
|
| - decodeUtf8(super.stdout).contains("#CRASHED - rendere");
|
| -
|
| - bool get _browserTestFailure {
|
| - // Browser tests fail unless stdout contains
|
| - // 'Content-Type: text/plain' followed by 'PASS'.
|
| - bool hasContentType = false;
|
| - var stdoutLines = decodeUtf8(super.stdout).split("\n");
|
| - var containsFail = false;
|
| - var containsPass = false;
|
| - for (String line in stdoutLines) {
|
| - switch (line) {
|
| - case 'Content-Type: text/plain':
|
| - hasContentType = true;
|
| - break;
|
| - case 'FAIL':
|
| - if (hasContentType) {
|
| - containsFail = true;
|
| - }
|
| - break;
|
| - case 'PASS':
|
| - if (hasContentType) {
|
| - containsPass = true;
|
| - }
|
| - break;
|
| - }
|
| - }
|
| - if (hasContentType) {
|
| - if (containsFail && containsPass) {
|
| - DebugLogger.warning("Test had 'FAIL' and 'PASS' in stdout. ($command)");
|
| - }
|
| - if (!containsFail && !containsPass) {
|
| - DebugLogger.warning("Test had neither 'FAIL' nor 'PASS' in stdout. "
|
| - "($command)");
|
| - return true;
|
| - }
|
| - if (containsFail) {
|
| - return true;
|
| - }
|
| - assert(containsPass);
|
| - if (exitCode != 0) {
|
| - var message = "All tests passed, but exitCode != 0. "
|
| - "Actual exitcode: $exitCode. "
|
| - "($command)";
|
| - DebugLogger.warning(message);
|
| - diagnostics.add(message);
|
| - }
|
| - return (!hasCrashed &&
|
| - exitCode != 0 &&
|
| - (!isWindows || exitCode != WHITELISTED_CONTENTSHELL_EXITCODE));
|
| - }
|
| - DebugLogger.warning("Couldn't find 'Content-Type: text/plain' in output. "
|
| - "($command).");
|
| - return true;
|
| - }
|
| -}
|
| -
|
| -class HTMLBrowserCommandOutputImpl extends BrowserCommandOutputImpl {
|
| - HTMLBrowserCommandOutputImpl(
|
| - command,
|
| - exitCode,
|
| - timedOut,
|
| - stdout,
|
| - stderr,
|
| - time,
|
| - compilationSkipped) :
|
| - super(command,
|
| - exitCode,
|
| - timedOut,
|
| - stdout,
|
| - stderr,
|
| - time,
|
| - compilationSkipped);
|
| -
|
| - bool didFail(TestCase testCase) {
|
| - return _getOutcome() != Expectation.PASS;
|
| - }
|
| -
|
| -
|
| - bool get _browserTestFailure {
|
| - // We should not need to convert back and forward.
|
| - var output = decodeUtf8(super.stdout);
|
| - if (output.contains("FAIL")) return true;
|
| - return !output.contains("PASS");
|
| - }
|
| -}
|
| -
|
| -class BrowserTestJsonResult {
|
| - static const ALLOWED_TYPES =
|
| - const ['sync_exception', 'window_onerror', 'script_onerror',
|
| - 'window_compilationerror', 'print', 'message_received', 'dom',
|
| - 'debug'];
|
| -
|
| - final Expectation outcome;
|
| - final String htmlDom;
|
| - final List events;
|
| -
|
| - BrowserTestJsonResult(this.outcome, this.htmlDom, this.events);
|
| -
|
| - static BrowserTestJsonResult parseFromString(String content) {
|
| - void validate(String assertion, bool value) {
|
| - if (!value) {
|
| - throw "InvalidFormat sent from browser driving page: $assertion:\n\n"
|
| - "$content";
|
| - }
|
| - }
|
| -
|
| - var events;
|
| - try {
|
| - events = JSON.decode(content);
|
| - if (events != null) {
|
| - validate("Message must be a List", events is List);
|
| -
|
| - Map<String, List<String>> messagesByType = {};
|
| - ALLOWED_TYPES.forEach((type) => messagesByType[type] = <String>[]);
|
| -
|
| - for (var entry in events) {
|
| - validate("An entry must be a Map", entry is Map);
|
| -
|
| - var type = entry['type'];
|
| - var value = entry['value'];
|
| - var timestamp = entry['timestamp'];
|
| -
|
| - validate("'type' of an entry must be a String",
|
| - type is String);
|
| - validate("'type' has to be in $ALLOWED_TYPES.",
|
| - ALLOWED_TYPES.contains(type));
|
| - validate("'timestamp' of an entry must be a number",
|
| - timestamp is num);
|
| -
|
| - messagesByType[type].add(value);
|
| - }
|
| - validate("The message must have exactly one 'dom' entry.",
|
| - messagesByType['dom'].length == 1);
|
| -
|
| - var dom = messagesByType['dom'][0];
|
| - if (dom.endsWith('\n')) {
|
| - dom = '$dom\n';
|
| - }
|
| -
|
| - return new BrowserTestJsonResult(
|
| - _getOutcome(messagesByType), dom, events);
|
| - }
|
| - } catch(error) {
|
| - // If something goes wrong, we know the content was not in the correct
|
| - // JSON format. So we can't parse it.
|
| - // The caller is responsible for falling back to the old way of
|
| - // determining if a test failed.
|
| - }
|
| -
|
| - return null;
|
| - }
|
| -
|
| - static Expectation _getOutcome(Map<String, List<String>> messagesByType) {
|
| - occured(type) => messagesByType[type].length > 0;
|
| - searchForMsg(types, message) {
|
| - return types.any((type) => messagesByType[type].contains(message));
|
| - }
|
| -
|
| - // FIXME(kustermann,ricow): I think this functionality doesn't work in
|
| - // test_controller.js: So far I haven't seen anything being reported on
|
| - // "window.compilationerror"
|
| - if (occured('window_compilationerror')) {
|
| - return Expectation.COMPILETIME_ERROR;
|
| - }
|
| -
|
| - if (occured('sync_exception') ||
|
| - occured('window_onerror') ||
|
| - occured('script_onerror')) {
|
| - return Expectation.RUNTIME_ERROR;
|
| - }
|
| -
|
| - if (messagesByType['dom'][0].contains('FAIL')) {
|
| - return Expectation.RUNTIME_ERROR;
|
| - }
|
| -
|
| - // We search for these messages in 'print' and 'message_received' because
|
| - // the unittest implementation posts these messages using
|
| - // "window.postMessage()" instead of the normal "print()" them.
|
| -
|
| - var isAsyncTest = searchForMsg(['print', 'message_received'],
|
| - 'unittest-suite-wait-for-done');
|
| - var isAsyncSuccess =
|
| - searchForMsg(['print', 'message_received'], 'unittest-suite-success') ||
|
| - searchForMsg(['print', 'message_received'], 'unittest-suite-done');
|
| -
|
| - if (isAsyncTest) {
|
| - if (isAsyncSuccess) {
|
| - return Expectation.PASS;
|
| - }
|
| - return Expectation.RUNTIME_ERROR;
|
| - }
|
| -
|
| - var mainStarted =
|
| - searchForMsg(['print', 'message_received'], 'dart-calling-main');
|
| - var mainDone =
|
| - searchForMsg(['print', 'message_received'], 'dart-main-done');
|
| -
|
| - if (mainStarted && mainDone) {
|
| - return Expectation.PASS;
|
| - }
|
| - return Expectation.FAIL;
|
| - }
|
| -}
|
| -
|
| -class BrowserControllerTestOutcome extends CommandOutputImpl
|
| - with UnittestSuiteMessagesMixin {
|
| - BrowserTestOutput _result;
|
| - Expectation _rawOutcome;
|
| -
|
| - factory BrowserControllerTestOutcome(Command command,
|
| - BrowserTestOutput result) {
|
| - void validate(String assertion, bool value) {
|
| - if (!value) {
|
| - throw "InvalidFormat sent from browser driving page: $assertion:\n\n"
|
| - "${result.lastKnownMessage}";
|
| - }
|
| - }
|
| -
|
| - String indent(String string, int numSpaces) {
|
| - var spaces = new List.filled(numSpaces, ' ').join('');
|
| - return string.replaceAll('\r\n', '\n')
|
| - .split('\n')
|
| - .map((line) => "$spaces$line")
|
| - .join('\n');
|
| - }
|
| -
|
| - String stdout = "";
|
| - String stderr = "";
|
| - Expectation outcome;
|
| -
|
| - var parsedResult =
|
| - BrowserTestJsonResult.parseFromString(result.lastKnownMessage);
|
| - if (parsedResult != null) {
|
| - outcome = parsedResult.outcome;
|
| - } else {
|
| - // Old way of determining whether a test failed or passed.
|
| - if (result.lastKnownMessage.contains("FAIL")) {
|
| - outcome = Expectation.RUNTIME_ERROR;
|
| - } else if (result.lastKnownMessage.contains("PASS")) {
|
| - outcome = Expectation.PASS;
|
| - } else {
|
| - outcome = Expectation.RUNTIME_ERROR;
|
| - }
|
| - }
|
| -
|
| - if (result.didTimeout) {
|
| - if (result.delayUntilTestStarted != null) {
|
| - stderr = "This test timed out. The delay until the test actually "
|
| - "started was: ${result.delayUntilTestStarted}.";
|
| - } else {
|
| - // TODO(ricow/kustermann) as soon as we record the state periodically,
|
| - // we will have more information and can remove this warning.
|
| - stderr = "This test has not notified test.py that it started running. "
|
| - "This could be a bug in test.py! "
|
| - "Please contact ricow/kustermann";
|
| - }
|
| - }
|
| -
|
| - if (parsedResult != null) {
|
| - stdout = "events:\n${indent(prettifyJson(parsedResult.events), 2)}\n\n";
|
| - } else {
|
| - stdout = "message:\n${indent(result.lastKnownMessage, 2)}\n\n";
|
| - }
|
| -
|
| - stderr =
|
| - '$stderr\n\n'
|
| - 'BrowserOutput while running the test (* EXPERIMENTAL *):\n'
|
| - 'BrowserOutput.stdout:\n'
|
| - '${indent(result.browserOutput.stdout.toString(), 2)}\n'
|
| - 'BrowserOutput.stderr:\n'
|
| - '${indent(result.browserOutput.stderr.toString(), 2)}\n'
|
| - '\n';
|
| - return new BrowserControllerTestOutcome._internal(
|
| - command, result, outcome, encodeUtf8(stdout), encodeUtf8(stderr));
|
| - }
|
| -
|
| - BrowserControllerTestOutcome._internal(
|
| - Command command, BrowserTestOutput result, this._rawOutcome,
|
| - List<int> stdout, List<int> stderr)
|
| - : super(command, 0, result.didTimeout, stdout, stderr, result.duration,
|
| - false, 0) {
|
| - _result = result;
|
| - }
|
| -
|
| - Expectation result(TestCase testCase) {
|
| - // Handle timeouts first
|
| - if (_result.didTimeout) return Expectation.TIMEOUT;
|
| -
|
| - // Multitests are handled specially
|
| - if (testCase.hasRuntimeError) {
|
| - if (_rawOutcome == Expectation.RUNTIME_ERROR) return Expectation.PASS;
|
| - return Expectation.MISSING_RUNTIME_ERROR;
|
| - }
|
| -
|
| - return _negateOutcomeIfNegativeTest(_rawOutcome, testCase.isNegative);
|
| - }
|
| -}
|
| -
|
| -
|
| -class AnalysisCommandOutputImpl extends CommandOutputImpl {
|
| - // An error line has 8 fields that look like:
|
| - // ERROR|COMPILER|MISSING_SOURCE|file:/tmp/t.dart|15|1|24|Missing source.
|
| - final int ERROR_LEVEL = 0;
|
| - final int ERROR_TYPE = 1;
|
| - final int FILENAME = 3;
|
| - final int FORMATTED_ERROR = 7;
|
| -
|
| - AnalysisCommandOutputImpl(command,
|
| - exitCode,
|
| - timedOut,
|
| - stdout,
|
| - stderr,
|
| - time,
|
| - compilationSkipped) :
|
| - super(command,
|
| - exitCode,
|
| - timedOut,
|
| - stdout,
|
| - stderr,
|
| - time,
|
| - compilationSkipped,
|
| - 0);
|
| -
|
| - Expectation result(TestCase testCase) {
|
| - // TODO(kustermann): If we run the analyzer not in batch mode, make sure
|
| - // that command.exitCodes matches 2 (errors), 1 (warnings), 0 (no warnings,
|
| - // no errors)
|
| -
|
| - // Handle crashes and timeouts first
|
| - if (hasCrashed) return Expectation.CRASH;
|
| - if (hasTimedOut) return Expectation.TIMEOUT;
|
| -
|
| - // Get the errors/warnings from the analyzer
|
| - List<String> errors = [];
|
| - List<String> warnings = [];
|
| - parseAnalyzerOutput(errors, warnings);
|
| -
|
| - // Handle errors / missing errors
|
| - if (testCase.expectCompileError) {
|
| - if (errors.length > 0) {
|
| - return Expectation.PASS;
|
| - }
|
| - return Expectation.MISSING_COMPILETIME_ERROR;
|
| - }
|
| - if (errors.length > 0) {
|
| - return Expectation.COMPILETIME_ERROR;
|
| - }
|
| -
|
| - // Handle static warnings / missing static warnings
|
| - if (testCase.hasStaticWarning) {
|
| - if (warnings.length > 0) {
|
| - return Expectation.PASS;
|
| - }
|
| - return Expectation.MISSING_STATIC_WARNING;
|
| - }
|
| - if (warnings.length > 0) {
|
| - return Expectation.STATIC_WARNING;
|
| - }
|
| -
|
| - assert (errors.length == 0 && warnings.length == 0);
|
| - assert (!testCase.hasCompileError &&
|
| - !testCase.hasStaticWarning);
|
| - return Expectation.PASS;
|
| - }
|
| -
|
| - void parseAnalyzerOutput(List<String> outErrors, List<String> outWarnings) {
|
| - // Parse a line delimited by the | character using \ as an escape charager
|
| - // like: FOO|BAR|FOO\|BAR|FOO\\BAZ as 4 fields: FOO BAR FOO|BAR FOO\BAZ
|
| - List<String> splitMachineError(String line) {
|
| - StringBuffer field = new StringBuffer();
|
| - List<String> result = [];
|
| - bool escaped = false;
|
| - for (var i = 0 ; i < line.length; i++) {
|
| - var c = line[i];
|
| - if (!escaped && c == '\\') {
|
| - escaped = true;
|
| - continue;
|
| - }
|
| - escaped = false;
|
| - if (c == '|') {
|
| - result.add(field.toString());
|
| - field = new StringBuffer();
|
| - continue;
|
| - }
|
| - field.write(c);
|
| - }
|
| - result.add(field.toString());
|
| - return result;
|
| - }
|
| -
|
| - for (String line in decodeUtf8(super.stderr).split("\n")) {
|
| - if (line.length == 0) continue;
|
| - List<String> fields = splitMachineError(line);
|
| - // We only consider errors/warnings for files of interest.
|
| - if (fields.length > FORMATTED_ERROR) {
|
| - if (fields[ERROR_LEVEL] == 'ERROR') {
|
| - outErrors.add(fields[FORMATTED_ERROR]);
|
| - } else if (fields[ERROR_LEVEL] == 'WARNING') {
|
| - outWarnings.add(fields[FORMATTED_ERROR]);
|
| - }
|
| - // OK to Skip error output that doesn't match the machine format
|
| - }
|
| - }
|
| - }
|
| -}
|
| -
|
| -class VmCommandOutputImpl extends CommandOutputImpl
|
| - with UnittestSuiteMessagesMixin {
|
| - static const DART_VM_EXITCODE_COMPILE_TIME_ERROR = 254;
|
| - static const DART_VM_EXITCODE_UNCAUGHT_EXCEPTION = 255;
|
| -
|
| - VmCommandOutputImpl(Command command, int exitCode, bool timedOut,
|
| - List<int> stdout, List<int> stderr, Duration time,
|
| - int pid)
|
| - : super(command, exitCode, timedOut, stdout, stderr, time, false, pid);
|
| -
|
| - Expectation result(TestCase testCase) {
|
| - // Handle crashes and timeouts first
|
| - if (hasCrashed) return Expectation.CRASH;
|
| - if (hasTimedOut) return Expectation.TIMEOUT;
|
| -
|
| - // Multitests are handled specially
|
| - if (testCase.expectCompileError) {
|
| - if (exitCode == DART_VM_EXITCODE_COMPILE_TIME_ERROR) {
|
| - return Expectation.PASS;
|
| - }
|
| - return Expectation.MISSING_COMPILETIME_ERROR;
|
| - }
|
| - if (testCase.hasRuntimeError) {
|
| - // TODO(kustermann): Do we consider a "runtimeError" only an uncaught
|
| - // exception or does any nonzero exit code fullfil this requirement?
|
| - if (exitCode != 0) {
|
| - return Expectation.PASS;
|
| - }
|
| - return Expectation.MISSING_RUNTIME_ERROR;
|
| - }
|
| -
|
| - // The actual outcome depends on the exitCode
|
| - Expectation outcome;
|
| - if (exitCode == DART_VM_EXITCODE_COMPILE_TIME_ERROR) {
|
| - outcome = Expectation.COMPILETIME_ERROR;
|
| - } else if (exitCode == DART_VM_EXITCODE_UNCAUGHT_EXCEPTION) {
|
| - outcome = Expectation.RUNTIME_ERROR;
|
| - } else if (exitCode != 0) {
|
| - // This is a general fail, in case we get an unknown nonzero exitcode.
|
| - outcome = Expectation.FAIL;
|
| - } else {
|
| - outcome = Expectation.PASS;
|
| - }
|
| - outcome = _negateOutcomeIfIncompleteAsyncTest(outcome, decodeUtf8(stdout));
|
| - return _negateOutcomeIfNegativeTest(outcome, testCase.isNegative);
|
| - }
|
| -}
|
| -
|
| -class CompilationCommandOutputImpl extends CommandOutputImpl {
|
| - static const DART2JS_EXITCODE_CRASH = 253;
|
| -
|
| - CompilationCommandOutputImpl(Command command, int exitCode, bool timedOut,
|
| - List<int> stdout, List<int> stderr, Duration time,
|
| - bool compilationSkipped)
|
| - : super(command, exitCode, timedOut, stdout, stderr, time,
|
| - compilationSkipped, 0);
|
| -
|
| - Expectation result(TestCase testCase) {
|
| - // Handle general crash/timeout detection.
|
| - if (hasCrashed) return Expectation.CRASH;
|
| - if (hasTimedOut) return Expectation.TIMEOUT;
|
| -
|
| - // Handle dart2js/dart2dart specific crash detection
|
| - if (exitCode == DART2JS_EXITCODE_CRASH ||
|
| - exitCode == VmCommandOutputImpl.DART_VM_EXITCODE_COMPILE_TIME_ERROR ||
|
| - exitCode == VmCommandOutputImpl.DART_VM_EXITCODE_UNCAUGHT_EXCEPTION) {
|
| - return Expectation.CRASH;
|
| - }
|
| -
|
| - // Multitests are handled specially
|
| - if (testCase.expectCompileError) {
|
| - // Nonzero exit code of the compiler means compilation failed
|
| - // TODO(kustermann): Do we have a special exit code in that case???
|
| - if (exitCode != 0) {
|
| - return Expectation.PASS;
|
| - }
|
| - return Expectation.MISSING_COMPILETIME_ERROR;
|
| - }
|
| -
|
| - // TODO(kustermann): This is a hack, remove it
|
| - if (testCase.hasRuntimeError && testCase.commands.length > 1) {
|
| - // We expected to run the test, but we got an compile time error.
|
| - // If the compilation succeeded, we wouldn't be in here!
|
| - assert(exitCode != 0);
|
| - return Expectation.COMPILETIME_ERROR;
|
| - }
|
| -
|
| - Expectation outcome =
|
| - exitCode == 0 ? Expectation.PASS : Expectation.COMPILETIME_ERROR;
|
| - return _negateOutcomeIfNegativeTest(outcome, testCase.isNegative);
|
| - }
|
| -}
|
| -
|
| -class JsCommandlineOutputImpl extends CommandOutputImpl
|
| - with UnittestSuiteMessagesMixin {
|
| - JsCommandlineOutputImpl(Command command, int exitCode, bool timedOut,
|
| - List<int> stdout, List<int> stderr, Duration time)
|
| - : super(command, exitCode, timedOut, stdout, stderr, time, false, 0);
|
| -
|
| - Expectation result(TestCase testCase) {
|
| - // Handle crashes and timeouts first
|
| - if (hasCrashed) return Expectation.CRASH;
|
| - if (hasTimedOut) return Expectation.TIMEOUT;
|
| -
|
| - if (testCase.hasRuntimeError) {
|
| - if (exitCode != 0) return Expectation.PASS;
|
| - return Expectation.MISSING_RUNTIME_ERROR;
|
| - }
|
| -
|
| - var outcome = exitCode == 0 ? Expectation.PASS : Expectation.RUNTIME_ERROR;
|
| - outcome = _negateOutcomeIfIncompleteAsyncTest(outcome, decodeUtf8(stdout));
|
| - return _negateOutcomeIfNegativeTest(outcome, testCase.isNegative);
|
| - }
|
| -}
|
| -
|
| -class PubCommandOutputImpl extends CommandOutputImpl {
|
| - PubCommandOutputImpl(PubCommand command, int exitCode, bool timedOut,
|
| - List<int> stdout, List<int> stderr, Duration time)
|
| - : super(command, exitCode, timedOut, stdout, stderr, time, false, 0);
|
| -
|
| - Expectation result(TestCase testCase) {
|
| - // Handle crashes and timeouts first
|
| - if (hasCrashed) return Expectation.CRASH;
|
| - if (hasTimedOut) return Expectation.TIMEOUT;
|
| -
|
| - if (exitCode == 0) {
|
| - return Expectation.PASS;
|
| - } else if ((command as PubCommand).command == 'get') {
|
| - return Expectation.PUB_GET_ERROR;
|
| - } else {
|
| - return Expectation.FAIL;
|
| - }
|
| - }
|
| -}
|
| -
|
| -class ScriptCommandOutputImpl extends CommandOutputImpl {
|
| - final Expectation _result;
|
| -
|
| - ScriptCommandOutputImpl(ScriptCommand command, this._result,
|
| - String scriptExecutionInformation, Duration time)
|
| - : super(command, 0, false, [], [], time, false, 0) {
|
| - var lines = scriptExecutionInformation.split("\n");
|
| - diagnostics.addAll(lines);
|
| - }
|
| -
|
| - Expectation result(TestCase testCase) => _result;
|
| -
|
| - bool get canRunDependendCommands => _result == Expectation.PASS;
|
| -
|
| - bool get successful => _result == Expectation.PASS;
|
| -
|
| -}
|
| -
|
| -CommandOutput createCommandOutput(Command command,
|
| - int exitCode,
|
| - bool timedOut,
|
| - List<int> stdout,
|
| - List<int> stderr,
|
| - Duration time,
|
| - bool compilationSkipped,
|
| - [int pid = 0]) {
|
| - if (command is ContentShellCommand) {
|
| - return new BrowserCommandOutputImpl(
|
| - command, exitCode, timedOut, stdout, stderr,
|
| - time, compilationSkipped);
|
| - } else if (command is BrowserTestCommand) {
|
| - return new HTMLBrowserCommandOutputImpl(
|
| - command, exitCode, timedOut, stdout, stderr,
|
| - time, compilationSkipped);
|
| - } else if (command is AnalysisCommand) {
|
| - return new AnalysisCommandOutputImpl(
|
| - command, exitCode, timedOut, stdout, stderr,
|
| - time, compilationSkipped);
|
| - } else if (command is VmCommand) {
|
| - return new VmCommandOutputImpl(
|
| - command, exitCode, timedOut, stdout, stderr, time, pid);
|
| - } else if (command is CompilationCommand) {
|
| - return new CompilationCommandOutputImpl(
|
| - command, exitCode, timedOut, stdout, stderr, time, compilationSkipped);
|
| - } else if (command is JSCommandlineCommand) {
|
| - return new JsCommandlineOutputImpl(
|
| - command, exitCode, timedOut, stdout, stderr, time);
|
| - } else if (command is PubCommand) {
|
| - return new PubCommandOutputImpl(
|
| - command, exitCode, timedOut, stdout, stderr, time);
|
| - }
|
| -
|
| - return new CommandOutputImpl(
|
| - command, exitCode, timedOut, stdout, stderr,
|
| - time, compilationSkipped, pid);
|
| -}
|
| -
|
| -
|
| /**
|
| * An OutputLog records the output from a test, but truncates it if
|
| * it is longer than MAX_HEAD characters, and just keeps the head and
|
| @@ -2620,50 +971,6 @@ class ReplayingCommandExecutor implements CommandExecutor {
|
| }
|
| }
|
|
|
| -bool shouldRetryCommand(CommandOutput output) {
|
| - var command = output.command;
|
| - // We rerun tests on Safari because 6.2 and 7.1 are flaky. Issue 21434.
|
| - if (command is BrowserTestCommand &&
|
| - command.retry &&
|
| - command.browser == 'safari' &&
|
| - output is BrowserControllerTestOutcome &&
|
| - output._rawOutcome != Expectation.PASS) {
|
| - return true;
|
| - }
|
| -
|
| - if (!output.successful) {
|
| - List<String> stdout, stderr;
|
| -
|
| - decodeOutput() {
|
| - if (stdout == null && stderr == null) {
|
| - stdout = decodeUtf8(output.stderr).split("\n");
|
| - stderr = decodeUtf8(output.stderr).split("\n");
|
| - }
|
| - }
|
| -
|
| - if (io.Platform.operatingSystem == 'linux') {
|
| - decodeOutput();
|
| - // No matter which command we ran: If we get failures due to the
|
| - // "xvfb-run" issue 7564, try re-running the test.
|
| - bool containsFailureMsg(String line) {
|
| - return line.contains(MESSAGE_CANNOT_OPEN_DISPLAY) ||
|
| - line.contains(MESSAGE_FAILED_TO_RUN_COMMAND);
|
| - }
|
| - if (stdout.any(containsFailureMsg) || stderr.any(containsFailureMsg)) {
|
| - return true;
|
| - }
|
| - }
|
| -
|
| - // We currently rerun dartium tests, see issue 14074.
|
| - if (command is BrowserTestCommand &&
|
| - command.retry &&
|
| - command.browser == 'dartium') {
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| -}
|
| -
|
| /*
|
| * [TestCaseCompleter] will listen for
|
| * NodeState.Processing -> NodeState.{Successful,Failed} state changes and
|
|
|