5882a0d13d
Change-Id: If98db1948e21c43eed60c7e1ee18c9fff61be88c Reviewed-on: https://dart-review.googlesource.com/40540 Commit-Queue: Peter von der Ahé <ahe@google.com> Commit-Queue: William Hesse <whesse@google.com> Reviewed-by: William Hesse <whesse@google.com>
678 lines
21 KiB
Dart
678 lines
21 KiB
Dart
// 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 'package:status_file/expectation.dart';
|
|
|
|
import 'command_output.dart';
|
|
import 'configuration.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,
|
|
List<Uri> bootstrapDependencies,
|
|
String executable,
|
|
List<String> arguments,
|
|
Map<String, String> environment,
|
|
{bool alwaysCompile: false,
|
|
String workingDirectory}) {
|
|
return new CompilationCommand._(displayName, outputFile, alwaysCompile,
|
|
bootstrapDependencies, executable, arguments, environment,
|
|
workingDirectory: workingDirectory);
|
|
}
|
|
|
|
static Command vmKernelCompilation(
|
|
String outputFile,
|
|
bool neverSkipCompilation,
|
|
List<Uri> bootstrapDependencies,
|
|
String executable,
|
|
List<String> arguments,
|
|
Map<String, String> environment) {
|
|
return new VMKernelCompilationCommand._(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 specParse(String executable, List<String> arguments,
|
|
Map<String, String> environmentOverrides) {
|
|
return new SpecParseCommand._(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 makeSymlink(String link, String target) {
|
|
return new MakeSymlinkCommand._(link, target);
|
|
}
|
|
|
|
static Command fasta(
|
|
Uri compilerLocation,
|
|
Uri outputFile,
|
|
List<Uri> bootstrapDependencies,
|
|
Uri executable,
|
|
List<String> arguments,
|
|
Map<String, String> environment,
|
|
Uri workingDirectory) {
|
|
return new FastaCompilationCommand._(
|
|
compilerLocation,
|
|
outputFile,
|
|
bootstrapDependencies,
|
|
executable,
|
|
arguments,
|
|
environment,
|
|
workingDirectory);
|
|
}
|
|
|
|
/// 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;
|
|
|
|
bool get outputIsUpToDate => 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";
|
|
}
|
|
|
|
bool get outputIsUpToDate => 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;
|
|
|
|
/// If true, then the compilation is run even if the input files are older
|
|
/// than the output file.
|
|
final bool _alwaysCompile;
|
|
final List<Uri> _bootstrapDependencies;
|
|
|
|
CompilationCommand._(
|
|
String displayName,
|
|
this._outputFile,
|
|
this._alwaysCompile,
|
|
this._bootstrapDependencies,
|
|
String executable,
|
|
List<String> arguments,
|
|
Map<String, String> environmentOverrides,
|
|
{String workingDirectory})
|
|
: super._(displayName, executable, arguments, environmentOverrides,
|
|
workingDirectory);
|
|
|
|
bool get outputIsUpToDate {
|
|
if (_alwaysCompile) return false;
|
|
|
|
var file = new io.File(new Path("$_outputFile.deps").toNativePath());
|
|
if (!file.existsSync()) return false;
|
|
|
|
var lines = file.readAsLinesSync();
|
|
var dependencies = <Uri>[];
|
|
for (var line in lines) {
|
|
line = line.trim();
|
|
if (line.isNotEmpty) {
|
|
dependencies.add(Uri.parse(line));
|
|
}
|
|
}
|
|
|
|
dependencies.addAll(_bootstrapDependencies);
|
|
var jsOutputLastModified = TestUtils.lastModifiedCache
|
|
.getLastModified(new Uri(scheme: 'file', path: _outputFile));
|
|
if (jsOutputLastModified == null) return false;
|
|
|
|
for (var dependency in dependencies) {
|
|
var dependencyLastModified =
|
|
TestUtils.lastModifiedCache.getLastModified(dependency);
|
|
if (dependencyLastModified == null ||
|
|
dependencyLastModified.isAfter(jsOutputLastModified)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void _buildHashCode(HashCodeBuilder builder) {
|
|
super._buildHashCode(builder);
|
|
builder.addJson(_outputFile);
|
|
builder.addJson(_alwaysCompile);
|
|
builder.addJson(_bootstrapDependencies);
|
|
}
|
|
|
|
bool _equal(CompilationCommand other) =>
|
|
super._equal(other) &&
|
|
_outputFile == other._outputFile &&
|
|
_alwaysCompile == other._alwaysCompile &&
|
|
deepJsonCompare(_bootstrapDependencies, other._bootstrapDependencies);
|
|
}
|
|
|
|
class FastaCompilationCommand extends CompilationCommand {
|
|
final Uri _compilerLocation;
|
|
|
|
FastaCompilationCommand._(
|
|
this._compilerLocation,
|
|
Uri outputFile,
|
|
List<Uri> bootstrapDependencies,
|
|
Uri executable,
|
|
List<String> arguments,
|
|
Map<String, String> environmentOverrides,
|
|
Uri workingDirectory)
|
|
: super._("fasta", outputFile.toFilePath(), true, bootstrapDependencies,
|
|
executable.toFilePath(), arguments, environmentOverrides,
|
|
workingDirectory: workingDirectory?.toFilePath());
|
|
|
|
@override
|
|
List<String> get batchArguments {
|
|
return <String>[
|
|
_compilerLocation.resolve("batch.dart").toFilePath(),
|
|
];
|
|
}
|
|
|
|
@override
|
|
String get reproductionCommand {
|
|
String relativizeAndEscape(String argument) {
|
|
if (workingDirectory != null) {
|
|
argument = argument.replaceAll(
|
|
workingDirectory, new Uri.directory(".").toFilePath());
|
|
}
|
|
return escapeCommandLineArgument(argument);
|
|
}
|
|
|
|
StringBuffer buffer = new StringBuffer();
|
|
if (workingDirectory != null && !io.Platform.isWindows) {
|
|
buffer.write("(cd ");
|
|
buffer.write(escapeCommandLineArgument(workingDirectory));
|
|
buffer.write(" ; ");
|
|
}
|
|
environmentOverrides?.forEach((key, value) {
|
|
if (io.Platform.isWindows) {
|
|
buffer.write("set ");
|
|
}
|
|
buffer.write(key);
|
|
buffer.write("=");
|
|
buffer.write(relativizeAndEscape(value));
|
|
if (io.Platform.isWindows) {
|
|
buffer.write(" &");
|
|
}
|
|
buffer.write(" ");
|
|
});
|
|
buffer.writeAll(
|
|
(<String>[executable]
|
|
..add(_compilerLocation.toFilePath())
|
|
..addAll(arguments))
|
|
.map(relativizeAndEscape),
|
|
" ");
|
|
if (workingDirectory != null) {
|
|
if (io.Platform.isWindows) {
|
|
buffer.write(" (working directory: $workingDirectory)");
|
|
} else {
|
|
buffer.write(" )");
|
|
}
|
|
}
|
|
return "$buffer";
|
|
}
|
|
|
|
@override
|
|
void _buildHashCode(HashCodeBuilder builder) {
|
|
super._buildHashCode(builder);
|
|
builder.addJson(_compilerLocation);
|
|
}
|
|
|
|
@override
|
|
bool _equal(FastaCompilationCommand other) {
|
|
return super._equal(other) && _compilerLocation == other._compilerLocation;
|
|
}
|
|
}
|
|
|
|
class VMKernelCompilationCommand extends CompilationCommand {
|
|
VMKernelCompilationCommand._(
|
|
String outputFile,
|
|
bool neverSkipCompilation,
|
|
List<Uri> bootstrapDependencies,
|
|
String executable,
|
|
List<String> arguments,
|
|
Map<String, String> environmentOverrides)
|
|
: super._('vm_compile_to_kernel', 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 SpecParseCommand extends ProcessCommand {
|
|
SpecParseCommand._(String executable, List<String> arguments,
|
|
Map<String, String> environmentOverrides)
|
|
: super._('spec_parser', 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);
|
|
}
|
|
|
|
/// [ScriptCommand]s are executed by dart code.
|
|
abstract class ScriptCommand extends Command {
|
|
ScriptCommand._(String displayName) : super._(displayName);
|
|
|
|
Future<ScriptCommandOutput> 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<ScriptCommandOutput> 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 ScriptCommandOutput(this, Expectation.pass, "", watch.elapsed);
|
|
}).catchError((error) {
|
|
return new ScriptCommandOutput(
|
|
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<ScriptCommandOutput> 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 ScriptCommandOutput(this, Expectation.pass, "", watch.elapsed);
|
|
}).catchError((error) {
|
|
return new ScriptCommandOutput(
|
|
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;
|
|
}
|