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 |