Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Unified Diff: tools/testing/dart/command.dart

Issue 2933973002: Simplify Command classes. (Closed)
Patch Set: Rename class. Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | tools/testing/dart/command_output.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/testing/dart/command.dart
diff --git a/tools/testing/dart/command.dart b/tools/testing/dart/command.dart
new file mode 100644
index 0000000000000000000000000000000000000000..e191186234fee1b96aad1d537be351553a014366
--- /dev/null
+++ b/tools/testing/dart/command.dart
@@ -0,0 +1,609 @@
+// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+// 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 'command_output.dart';
+import 'configuration.dart';
+import 'expectation.dart';
+import 'path.dart';
+import 'utils.dart';
+
+/// A command executed as a step in a test case.
+class Command {
+ static Command contentShell(
+ String executable,
+ String htmlFile,
+ List<String> options,
+ List<String> dartFlags,
+ Map<String, String> environment) {
+ return new ContentShellCommand._(
+ executable, htmlFile, options, dartFlags, environment);
+ }
+
+ static Command browserTest(String url, Configuration configuration,
+ {bool retry}) {
+ return new BrowserTestCommand._(url, configuration, retry);
+ }
+
+ static Command browserHtmlTest(
+ String url, Configuration configuration, List<String> expectedMessages,
+ {bool retry}) {
+ return new BrowserHtmlTestCommand._(
+ url, configuration, expectedMessages, retry);
+ }
+
+ static Command compilation(
+ String displayName,
+ String outputFile,
+ bool neverSkipCompilation,
+ List<Uri> bootstrapDependencies,
+ String executable,
+ List<String> arguments,
+ Map<String, String> environment) {
+ return new CompilationCommand._(
+ displayName,
+ outputFile,
+ neverSkipCompilation,
+ bootstrapDependencies,
+ executable,
+ arguments,
+ environment);
+ }
+
+ static Command kernelCompilation(
+ String outputFile,
+ bool neverSkipCompilation,
+ List<Uri> bootstrapDependencies,
+ String executable,
+ List<String> arguments,
+ Map<String, String> environment) {
+ return new KernelCompilationCommand._(outputFile, neverSkipCompilation,
+ bootstrapDependencies, executable, arguments, environment);
+ }
+
+ static Command analysis(String executable, List<String> arguments,
+ Map<String, String> environmentOverrides) {
+ return new AnalysisCommand._(executable, arguments, environmentOverrides);
+ }
+
+ static Command vm(String executable, List<String> arguments,
+ Map<String, String> environmentOverrides) {
+ return new VmCommand._(executable, arguments, environmentOverrides);
+ }
+
+ static Command vmBatch(String executable, String tester,
+ List<String> arguments, Map<String, String> environmentOverrides,
+ {bool checked: true}) {
+ return new VmBatchCommand._(
+ executable, tester, arguments, environmentOverrides,
+ checked: checked);
+ }
+
+ static Command adbPrecompiled(String precompiledRunner, String processTest,
+ String testDirectory, List<String> arguments, bool useBlobs) {
+ return new AdbPrecompilationCommand._(
+ precompiledRunner, processTest, testDirectory, arguments, useBlobs);
+ }
+
+ static Command jsCommandLine(
+ String displayName, String executable, List<String> arguments,
+ [Map<String, String> environment]) {
+ return new JSCommandlineCommand._(
+ displayName, executable, arguments, environment);
+ }
+
+ static Command process(
+ String displayName, String executable, List<String> arguments,
+ [Map<String, String> environment, String workingDirectory]) {
+ return new ProcessCommand._(
+ displayName, executable, arguments, environment, workingDirectory);
+ }
+
+ static Command copy(String sourceDirectory, String destinationDirectory) {
+ return new CleanDirectoryCopyCommand._(
+ sourceDirectory, destinationDirectory);
+ }
+
+ static Command pub(String pubCommand, String pubExecutable,
+ String pubspecYamlDirectory, String pubCacheDirectory,
+ {List<String> arguments: const <String>[]}) {
+ return new PubCommand._(pubCommand, pubExecutable, pubspecYamlDirectory,
+ pubCacheDirectory, arguments);
+ }
+
+ static Command makeSymlink(String link, String target) {
+ return new MakeSymlinkCommand._(link, target);
+ }
+
+ /// A descriptive name for this command.
+ final 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 ==(Object other) =>
+ identical(this, other) ||
+ (runtimeType == other.runtimeType && _equal(other as Command));
+
+ void _buildHashCode(HashCodeBuilder builder) {
+ builder.addJson(displayName);
+ }
+
+ bool _equal(covariant 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.
+ final List<String> arguments;
+
+ /// Environment for the command.
+ final Map<String, String> environmentOverrides;
+
+ /// Working directory for the command.
+ final String workingDirectory;
+
+ ProcessCommand._(String displayName, this.executable, this.arguments,
+ [this.environmentOverrides, this.workingDirectory])
+ : 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 env = new StringBuffer();
+ environmentOverrides?.forEach((key, value) =>
+ (io.Platform.operatingSystem == 'windows')
+ ? env.write('set $key=${escapeCommandLineArgument(value)} & ')
+ : env.write('$key=${escapeCommandLineArgument(value)} '));
+ var command = ([executable]..addAll(batchArguments)..addAll(arguments))
+ .map(escapeCommandLineArgument)
+ .join(' ');
+ if (workingDirectory != null) {
+ command = "$command (working directory: $workingDirectory)";
+ }
+ return "$env$command";
+ }
+
+ Future<bool> get outputIsUpToDate => new Future.value(false);
+
+ /// Arguments that are passed to the process when starting batch mode.
+ ///
+ /// In non-batch mode, they should be passed before [arguments].
+ List<String> get batchArguments => const [];
+}
+
+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);
+}
+
+class KernelCompilationCommand extends CompilationCommand {
+ KernelCompilationCommand._(
+ String outputFile,
+ bool neverSkipCompilation,
+ List<Uri> bootstrapDependencies,
+ String executable,
+ List<String> arguments,
+ Map<String, String> environmentOverrides)
+ : super._('dartk', outputFile, neverSkipCompilation,
+ bootstrapDependencies, executable, arguments, environmentOverrides);
+
+ int get maxNumRetries => 1;
+}
+
+/// 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 ==(Object 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<String, String>> environments = {};
+
+ static Map<String, String> _getEnvironment(
+ Map<String, String> env, List<String> dartFlags) {
+ var needDartFlags = dartFlags != null && dartFlags.isNotEmpty;
+ if (needDartFlags) {
+ if (env == null) {
+ env = const <String, String>{};
+ }
+ var flags = dartFlags.join(' ');
+ return environments.putIfAbsent(
+ new AddFlagsKey(flags, env),
+ () => new Map<String, String>.from(env)
+ ..addAll({'DART_FLAGS': flags, 'DART_FORWARDING_PRINT': '1'}));
+ }
+ return env;
+ }
+
+ static List<String> _getArguments(List<String> options, String htmlFile) {
+ var arguments = options.toList();
+ arguments.add(htmlFile);
+ return arguments;
+ }
+
+ int get maxNumRetries => 3;
+}
+
+class BrowserTestCommand extends Command {
+ Runtime get browser => configuration.runtime;
+ final String url;
+ final Configuration configuration;
+ final bool retry;
+
+ BrowserTestCommand._(this.url, this.configuration, this.retry)
+ : super._(configuration.runtime.name);
+
+ void _buildHashCode(HashCodeBuilder builder) {
+ super._buildHashCode(builder);
+ builder.addJson(browser.name);
+ 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 = [
+ io.Platform.resolvedExecutable,
+ 'tools/testing/dart/launch_browser.dart',
+ browser.name,
+ url
+ ];
+ return parts.map(escapeCommandLineArgument).join(' ');
+ }
+
+ int get maxNumRetries => 4;
+}
+
+class BrowserHtmlTestCommand extends BrowserTestCommand {
+ List<String> expectedMessages;
+ BrowserHtmlTestCommand._(String url, Configuration configuration,
+ this.expectedMessages, bool retry)
+ : super._(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 {
+ AnalysisCommand._(String executable, List<String> arguments,
+ Map<String, String> environmentOverrides)
+ : super._('dart2analyzer', executable, arguments, environmentOverrides);
+}
+
+class VmCommand extends ProcessCommand {
+ VmCommand._(String executable, List<String> arguments,
+ Map<String, String> environmentOverrides)
+ : super._('vm', executable, arguments, environmentOverrides);
+}
+
+class VmBatchCommand extends ProcessCommand implements VmCommand {
+ final String dartFile;
+ final bool checked;
+
+ VmBatchCommand._(String executable, String dartFile, List<String> arguments,
+ Map<String, String> environmentOverrides,
+ {this.checked: true})
+ : this.dartFile = dartFile,
+ super._('vm-batch', executable, arguments, environmentOverrides);
+
+ @override
+ List<String> get batchArguments =>
+ checked ? ['--checked', dartFile] : [dartFile];
+
+ @override
+ bool _equal(VmBatchCommand other) {
+ return super._equal(other) &&
+ dartFile == other.dartFile &&
+ checked == other.checked;
+ }
+
+ @override
+ void _buildHashCode(HashCodeBuilder builder) {
+ super._buildHashCode(builder);
+ builder.addJson(dartFile);
+ builder.addJson(checked);
+ }
+}
+
+class AdbPrecompilationCommand extends Command {
+ final String precompiledRunnerFilename;
+ final String processTestFilename;
+ final String precompiledTestDirectory;
+ final List<String> arguments;
+ final bool useBlobs;
+
+ AdbPrecompilationCommand._(
+ this.precompiledRunnerFilename,
+ this.processTestFilename,
+ this.precompiledTestDirectory,
+ this.arguments,
+ this.useBlobs)
+ : super._("adb_precompilation");
+
+ void _buildHashCode(HashCodeBuilder builder) {
+ super._buildHashCode(builder);
+ builder.add(precompiledRunnerFilename);
+ builder.add(precompiledTestDirectory);
+ builder.add(arguments);
+ builder.add(useBlobs);
+ }
+
+ bool _equal(AdbPrecompilationCommand other) =>
+ super._equal(other) &&
+ precompiledRunnerFilename == other.precompiledRunnerFilename &&
+ useBlobs == other.useBlobs &&
+ arguments == other.arguments &&
+ precompiledTestDirectory == other.precompiledTestDirectory;
+
+ String toString() => 'Steps to push precompiled runner and precompiled code '
+ 'to an attached device. Uses (and requires) adb.';
+}
+
+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, List<String> args)
+ : command = pubCommand,
+ super._(
+ 'pub_$pubCommand',
+ new io.File(pubExecutable).absolute.path,
+ [pubCommand]..addAll(args),
+ {'PUB_CACHE': pubCacheDirectory},
+ pubspecYamlDirectory);
+
+ 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) {
+ Future 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;
+}
+
+/// 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;
+}
« no previous file with comments | « no previous file | tools/testing/dart/command_output.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698