bd0b10802b
This CL is part of an effort to bump the SDK requirement to `3.12.0-0` for all the packages in `pkg` that are not published to `pub`, so that we can get better testing of the "private named parameters" feature. (Packages that *are* published to `pub` can't be safely bumped yet, because SDK 3.12 hasn't been released, and I don't want to block those packages' ability to publish useful updates to customers.) This change covers the following packages, which are owned by OWNERS_INFRA: - pkg/smith - pkg/status_file - pkg/test_runner Changes to `pubspec.yaml` files were made manually. Changes to `.dart` files were made automatically (with some exceptions), using `dart fix` to migrate to using private named parameters where it is possible to do so without changing semantics. Note that this migration is conservative; see https://github.com/dart-lang/sdk/issues/58607 for details. The exceptions are: - pkg/test_runner/lib/src/compiler_configuration.dart - pkg/test_runner/lib/src/path.dart These files contained code that triggered the `use_null_aware_elements` lint, so I manually fixed the instances of the lint. (The lint didn't previously fire because the test runner used to be on language version 3.5.0, which was before the "null aware elements" feature existed.) Change-Id: Ia5d99ba5fc2d5aacbec5b091348be1d26a6a6964 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/487882 Commit-Queue: Paul Berry <paulberry@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
985 lines
30 KiB
Dart
985 lines
30 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 'command_output.dart';
|
|
import 'configuration.dart';
|
|
import 'path.dart';
|
|
import 'test_case.dart';
|
|
import 'utils.dart';
|
|
|
|
/// A command executed as a step in a test case.
|
|
abstract class Command {
|
|
/// A descriptive name for this command.
|
|
final String displayName;
|
|
|
|
/// When cloning a command object to run it multiple times, we give
|
|
/// the different copies distinct values for index.
|
|
final int index;
|
|
|
|
/// Number of times this command *can* be retried.
|
|
int get maxNumRetries => 2;
|
|
|
|
/// Reproduction command.
|
|
String get reproductionCommand;
|
|
|
|
/// 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, {this.index = 0});
|
|
|
|
/// A virtual clone method for a member of the Command hierarchy.
|
|
/// Two clones with the same index will be equal, with different indices
|
|
/// will be distinct. Used to run tests multiple times, since identical
|
|
/// commands are only run once by the dependency graph scheduler.
|
|
Command indexedCopy(int index);
|
|
|
|
CommandOutput createOutput(int exitCode, bool timedOut, List<int> stdout,
|
|
List<int> stderr, Duration time, bool compilationSkipped,
|
|
[int pid = 0]) =>
|
|
CommandOutput(this, exitCode, timedOut, stdout, stderr, time,
|
|
compilationSkipped, pid);
|
|
|
|
@override
|
|
int get hashCode {
|
|
if (_cachedHashCode == null) {
|
|
var builder = HashCodeBuilder();
|
|
_buildHashCode(builder);
|
|
_cachedHashCode = builder.value;
|
|
}
|
|
return _cachedHashCode!;
|
|
}
|
|
|
|
@override
|
|
operator ==(Object other) =>
|
|
identical(this, other) ||
|
|
(runtimeType == other.runtimeType && _equal(other as Command));
|
|
|
|
void _buildHashCode(HashCodeBuilder builder) {
|
|
builder.add(displayName);
|
|
builder.add(index);
|
|
}
|
|
|
|
bool _equal(covariant Command other) =>
|
|
hashCode == other.hashCode &&
|
|
displayName == other.displayName &&
|
|
index == other.index;
|
|
|
|
@override
|
|
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(super.displayName, this.executable, this.arguments,
|
|
[this.environmentOverrides = const {},
|
|
this.workingDirectory,
|
|
int index = 0])
|
|
: super._(index: index) {
|
|
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('/', '\\');
|
|
}
|
|
}
|
|
|
|
@override
|
|
ProcessCommand indexedCopy(int index) {
|
|
return ProcessCommand(displayName, executable, arguments,
|
|
environmentOverrides, workingDirectory, index);
|
|
}
|
|
|
|
@override
|
|
void _buildHashCode(HashCodeBuilder builder) {
|
|
super._buildHashCode(builder);
|
|
builder.addJson(executable);
|
|
builder.addJson(workingDirectory);
|
|
builder.addJson(arguments);
|
|
builder.addJson(environmentOverrides);
|
|
}
|
|
|
|
@override
|
|
bool _equal(ProcessCommand other) =>
|
|
super._equal(other) &&
|
|
executable == other.executable &&
|
|
deepJsonCompare(arguments, other.arguments) &&
|
|
workingDirectory == other.workingDirectory &&
|
|
deepJsonCompare(environmentOverrides, other.environmentOverrides);
|
|
|
|
@override
|
|
String get reproductionCommand {
|
|
var env = StringBuffer();
|
|
environmentOverrides.forEach((key, value) =>
|
|
(io.Platform.operatingSystem == 'windows')
|
|
? env.write('set $key=${escapeCommandLineArgument(value)} & ')
|
|
: env.write('$key=${escapeCommandLineArgument(value)} '));
|
|
var command = [executable, ...nonBatchArguments, ...arguments]
|
|
.map(escapeCommandLineArgument)
|
|
.join(' ');
|
|
if (workingDirectory != null) {
|
|
command = "$command (working directory: $workingDirectory)";
|
|
}
|
|
return "$env$command";
|
|
}
|
|
|
|
@override
|
|
bool get outputIsUpToDate => false;
|
|
|
|
/// Additional arguments to prepend before [arguments] when running the
|
|
/// process in batch mode.
|
|
List<String> get batchArguments => const [];
|
|
|
|
/// Additional arguments to prepend before [arguments] when running the
|
|
/// process in non-batch mode.
|
|
List<String> get nonBatchArguments => const [];
|
|
}
|
|
|
|
class CompilationCommand extends ProcessCommand {
|
|
/// The primary output file that will be created by this command.
|
|
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._bootstrapDependencies,
|
|
String executable,
|
|
List<String> arguments,
|
|
Map<String, String> environmentOverrides,
|
|
{required this._alwaysCompile,
|
|
String? workingDirectory,
|
|
int index = 0})
|
|
: super(displayName, executable, arguments, environmentOverrides,
|
|
workingDirectory, index);
|
|
|
|
@override
|
|
CompilationCommand indexedCopy(int index) => CompilationCommand(
|
|
displayName,
|
|
outputFile,
|
|
_bootstrapDependencies,
|
|
executable,
|
|
arguments,
|
|
environmentOverrides,
|
|
alwaysCompile: _alwaysCompile,
|
|
workingDirectory: workingDirectory,
|
|
index: index);
|
|
|
|
@override
|
|
CommandOutput createOutput(int exitCode, bool timedOut, List<int> stdout,
|
|
List<int> stderr, Duration time, bool compilationSkipped,
|
|
[int pid = 0]) {
|
|
if (displayName == 'precompiler' ||
|
|
displayName == 'app_jit' ||
|
|
displayName == 'dart2bytecode' ||
|
|
displayName == 'modaot') {
|
|
return VMCommandOutput(
|
|
this, exitCode, timedOut, stdout, stderr, time, pid);
|
|
} else if (displayName == 'dart2wasm') {
|
|
return Dart2WasmCompilerCommandOutput(
|
|
this, exitCode, timedOut, stdout, stderr, time, compilationSkipped);
|
|
}
|
|
|
|
return CompilationCommandOutput(
|
|
this, exitCode, timedOut, stdout, stderr, time, compilationSkipped);
|
|
}
|
|
|
|
@override
|
|
bool get outputIsUpToDate {
|
|
if (_alwaysCompile) return false;
|
|
|
|
var file = io.File(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(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;
|
|
}
|
|
|
|
@override
|
|
List<String> get batchArguments {
|
|
return [...arguments.where((arg) => arg.startsWith('--enable-experiment'))];
|
|
}
|
|
|
|
@override
|
|
void _buildHashCode(HashCodeBuilder builder) {
|
|
super._buildHashCode(builder);
|
|
builder.addJson(outputFile);
|
|
builder.addJson(_alwaysCompile);
|
|
builder.addJson(_bootstrapDependencies);
|
|
}
|
|
|
|
@override
|
|
bool _equal(CompilationCommand other) =>
|
|
super._equal(other) &&
|
|
outputFile == other.outputFile &&
|
|
_alwaysCompile == other._alwaysCompile &&
|
|
deepJsonCompare(_bootstrapDependencies, other._bootstrapDependencies);
|
|
}
|
|
|
|
class Dart2jsCompilationCommand extends CompilationCommand {
|
|
final bool useSdk;
|
|
|
|
Dart2jsCompilationCommand(
|
|
String outputFile,
|
|
List<Uri> bootstrapDependencies,
|
|
String executable,
|
|
List<String> arguments,
|
|
Map<String, String> environmentOverrides,
|
|
{required this.useSdk,
|
|
required super.alwaysCompile,
|
|
super.workingDirectory,
|
|
super.index = 0})
|
|
: super("dart2js", outputFile, bootstrapDependencies, executable,
|
|
arguments, environmentOverrides);
|
|
|
|
@override
|
|
Dart2jsCompilationCommand indexedCopy(int index) => Dart2jsCompilationCommand(
|
|
outputFile,
|
|
_bootstrapDependencies,
|
|
executable,
|
|
arguments,
|
|
environmentOverrides,
|
|
useSdk: useSdk,
|
|
alwaysCompile: _alwaysCompile,
|
|
workingDirectory: workingDirectory,
|
|
index: index);
|
|
|
|
@override
|
|
CommandOutput createOutput(int exitCode, bool timedOut, List<int> stdout,
|
|
List<int> stderr, Duration time, bool compilationSkipped,
|
|
[int? pid = 0]) {
|
|
return Dart2jsCompilerCommandOutput(
|
|
this, exitCode, timedOut, stdout, stderr, time, compilationSkipped);
|
|
}
|
|
|
|
@override
|
|
List<String> get batchArguments {
|
|
return <String>[
|
|
if (useSdk) ...['compile', 'js'],
|
|
...super.batchArguments,
|
|
];
|
|
}
|
|
|
|
@override
|
|
List<String> get nonBatchArguments {
|
|
return <String>[
|
|
if (useSdk) ...['compile', 'js'],
|
|
...super.nonBatchArguments,
|
|
];
|
|
}
|
|
|
|
@override
|
|
void _buildHashCode(HashCodeBuilder builder) {
|
|
super._buildHashCode(builder);
|
|
builder.addJson(useSdk);
|
|
}
|
|
|
|
@override
|
|
bool _equal(Dart2jsCompilationCommand other) {
|
|
return super._equal(other) && useSdk == other.useSdk;
|
|
}
|
|
}
|
|
|
|
class DevCompilerCompilationCommand extends CompilationCommand {
|
|
final String compilerPath;
|
|
|
|
final bool enableHostAsserts;
|
|
|
|
DevCompilerCompilationCommand(
|
|
String outputFile,
|
|
List<Uri> bootstrapDependencies,
|
|
String executable,
|
|
List<String> arguments,
|
|
Map<String, String> environmentOverrides,
|
|
{required this.compilerPath,
|
|
required super.alwaysCompile,
|
|
required this.enableHostAsserts,
|
|
super.workingDirectory,
|
|
super.index = 0})
|
|
: super("ddc", outputFile, bootstrapDependencies, executable, arguments,
|
|
environmentOverrides);
|
|
|
|
@override
|
|
DevCompilerCompilationCommand indexedCopy(int index) =>
|
|
DevCompilerCompilationCommand(outputFile, _bootstrapDependencies,
|
|
executable, arguments, environmentOverrides,
|
|
compilerPath: compilerPath,
|
|
alwaysCompile: _alwaysCompile,
|
|
enableHostAsserts: enableHostAsserts,
|
|
workingDirectory: workingDirectory,
|
|
index: index);
|
|
|
|
@override
|
|
CommandOutput createOutput(int exitCode, bool timedOut, List<int> stdout,
|
|
List<int> stderr, Duration time, bool compilationSkipped,
|
|
[int pid = 0]) {
|
|
return DevCompilerCommandOutput(this, exitCode, timedOut, stdout, stderr,
|
|
time, compilationSkipped, pid);
|
|
}
|
|
|
|
@override
|
|
List<String> get batchArguments {
|
|
return <String>[
|
|
if (enableHostAsserts) '--enable-asserts',
|
|
compilerPath,
|
|
...super.batchArguments,
|
|
];
|
|
}
|
|
|
|
@override
|
|
List<String> get nonBatchArguments {
|
|
return <String>[
|
|
if (enableHostAsserts) '--enable-asserts',
|
|
compilerPath,
|
|
...super.nonBatchArguments,
|
|
];
|
|
}
|
|
|
|
@override
|
|
void _buildHashCode(HashCodeBuilder builder) {
|
|
super._buildHashCode(builder);
|
|
builder.addJson(compilerPath);
|
|
}
|
|
|
|
@override
|
|
bool _equal(DevCompilerCompilationCommand other) {
|
|
return super._equal(other) && compilerPath == other.compilerPath;
|
|
}
|
|
}
|
|
|
|
class FastaCompilationCommand extends CompilationCommand {
|
|
final Uri _compilerLocation;
|
|
|
|
FastaCompilationCommand(
|
|
this._compilerLocation,
|
|
String outputFile,
|
|
List<Uri> bootstrapDependencies,
|
|
String executable,
|
|
List<String> arguments,
|
|
Map<String, String> environmentOverrides,
|
|
String? workingDirectory,
|
|
{super.index = 0})
|
|
: super("fasta", outputFile, bootstrapDependencies, executable, arguments,
|
|
environmentOverrides,
|
|
alwaysCompile: true, workingDirectory: workingDirectory);
|
|
|
|
@override
|
|
FastaCompilationCommand indexedCopy(int index) => FastaCompilationCommand(
|
|
_compilerLocation,
|
|
outputFile,
|
|
_bootstrapDependencies,
|
|
executable,
|
|
arguments,
|
|
environmentOverrides,
|
|
workingDirectory,
|
|
index: index);
|
|
|
|
@override
|
|
FastaCommandOutput createOutput(int exitCode, bool timedOut, List<int> stdout,
|
|
List<int> stderr, Duration time, bool compilationSkipped,
|
|
[int? pid = 0]) =>
|
|
FastaCommandOutput(
|
|
this, exitCode, timedOut, stdout, stderr, time, compilationSkipped);
|
|
|
|
@override
|
|
List<String> get batchArguments {
|
|
return <String>[
|
|
...super.batchArguments,
|
|
'--enable-asserts',
|
|
_compilerLocation.resolve("batch.dart").toFilePath(),
|
|
];
|
|
}
|
|
|
|
@override
|
|
String get reproductionCommand {
|
|
String relativizeAndEscape(String argument) {
|
|
if (workingDirectory != null) {
|
|
argument = argument.replaceAll(
|
|
workingDirectory!, Uri.directory(".").toFilePath());
|
|
}
|
|
return escapeCommandLineArgument(argument);
|
|
}
|
|
|
|
var buffer = 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(
|
|
[executable, _compilerLocation.toFilePath(), ...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,
|
|
List<Uri> bootstrapDependencies,
|
|
String executable,
|
|
List<String> arguments,
|
|
Map<String, String> environmentOverrides,
|
|
{required super.alwaysCompile,
|
|
super.index = 0})
|
|
: super('vm_compile_to_kernel', outputFile, bootstrapDependencies,
|
|
executable, arguments, environmentOverrides);
|
|
|
|
@override
|
|
VMKernelCompilationCommand indexedCopy(int index) =>
|
|
VMKernelCompilationCommand(outputFile, _bootstrapDependencies, executable,
|
|
arguments, environmentOverrides,
|
|
alwaysCompile: _alwaysCompile, index: index);
|
|
|
|
@override
|
|
VMKernelCompilationCommandOutput createOutput(
|
|
int exitCode,
|
|
bool timedOut,
|
|
List<int> stdout,
|
|
List<int> stderr,
|
|
Duration time,
|
|
bool compilationSkipped,
|
|
[int? pid = 0]) =>
|
|
VMKernelCompilationCommandOutput(
|
|
this, exitCode, timedOut, stdout, stderr, time, compilationSkipped);
|
|
|
|
@override
|
|
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
|
|
@override
|
|
bool operator ==(Object other) =>
|
|
other is AddFlagsKey && flags == other.flags && env == other.env;
|
|
@override
|
|
int get hashCode => flags.hashCode ^ env.hashCode;
|
|
}
|
|
|
|
class BrowserTestCommand extends Command {
|
|
Runtime get browser => configuration.runtime;
|
|
final String url;
|
|
final TestConfiguration configuration;
|
|
|
|
BrowserTestCommand(this.url, this.configuration, {super.index = 0})
|
|
: super._(configuration.runtime.name);
|
|
|
|
@override
|
|
BrowserTestCommand indexedCopy(int index) =>
|
|
BrowserTestCommand(url, configuration, index: index);
|
|
|
|
@override
|
|
void _buildHashCode(HashCodeBuilder builder) {
|
|
super._buildHashCode(builder);
|
|
builder.addJson(browser.name);
|
|
builder.addJson(url);
|
|
builder.add(configuration);
|
|
}
|
|
|
|
@override
|
|
bool _equal(BrowserTestCommand other) =>
|
|
super._equal(other) &&
|
|
browser == other.browser &&
|
|
url == other.url &&
|
|
identical(configuration, other.configuration);
|
|
|
|
@override
|
|
String get reproductionCommand {
|
|
var parts = [
|
|
io.Platform.resolvedExecutable,
|
|
'pkg/test_runner/bin/launch_browser.dart',
|
|
browser.name,
|
|
url
|
|
];
|
|
return parts.map(escapeCommandLineArgument).join(' ');
|
|
}
|
|
|
|
@override
|
|
int get maxNumRetries => 4;
|
|
}
|
|
|
|
class AnalysisCommand extends ProcessCommand {
|
|
final List<String> commonAnalyzerCliArguments;
|
|
|
|
AnalysisCommand(String executable, List<String> arguments,
|
|
this.commonAnalyzerCliArguments, Map<String, String> environmentOverrides,
|
|
{int index = 0})
|
|
: super('dart2analyzer', executable, arguments, environmentOverrides,
|
|
null, index);
|
|
|
|
@override
|
|
AnalysisCommand indexedCopy(int index) => AnalysisCommand(
|
|
executable, arguments, commonAnalyzerCliArguments, environmentOverrides,
|
|
index: index);
|
|
|
|
@override
|
|
CommandOutput createOutput(int exitCode, bool timedOut, List<int> stdout,
|
|
List<int> stderr, Duration time, bool compilationSkipped,
|
|
[int? pid = 0]) =>
|
|
AnalysisCommandOutput(
|
|
this, exitCode, timedOut, stdout, stderr, time, compilationSkipped);
|
|
|
|
@override
|
|
List<String> get batchArguments => commonAnalyzerCliArguments;
|
|
|
|
@override
|
|
List<String> get nonBatchArguments => commonAnalyzerCliArguments;
|
|
|
|
@override
|
|
bool _equal(covariant ProcessCommand other) {
|
|
return other is AnalysisCommand &&
|
|
super._equal(other) &&
|
|
deepJsonCompare(
|
|
commonAnalyzerCliArguments, other.commonAnalyzerCliArguments);
|
|
}
|
|
}
|
|
|
|
class CompareAnalyzerCfeCommand extends ProcessCommand {
|
|
CompareAnalyzerCfeCommand(String executable, List<String> arguments,
|
|
Map<String, String> environmentOverrides, {int index = 0})
|
|
: super('compare_analyzer_cfe', executable, arguments,
|
|
environmentOverrides, null, index);
|
|
|
|
@override
|
|
CompareAnalyzerCfeCommand indexedCopy(int index) =>
|
|
CompareAnalyzerCfeCommand(executable, arguments, environmentOverrides,
|
|
index: index);
|
|
|
|
@override
|
|
CompareAnalyzerCfeCommandOutput createOutput(
|
|
int exitCode,
|
|
bool timedOut,
|
|
List<int> stdout,
|
|
List<int> stderr,
|
|
Duration time,
|
|
bool compilationSkipped,
|
|
[int? pid = 0]) =>
|
|
CompareAnalyzerCfeCommandOutput(
|
|
this, exitCode, timedOut, stdout, stderr, time, compilationSkipped);
|
|
}
|
|
|
|
class SpecParseCommand extends ProcessCommand {
|
|
SpecParseCommand(String executable, List<String> arguments,
|
|
Map<String, String> environmentOverrides, {int index = 0})
|
|
: super('spec_parser', executable, arguments, environmentOverrides, null,
|
|
index);
|
|
|
|
@override
|
|
SpecParseCommand indexedCopy(int index) =>
|
|
SpecParseCommand(executable, arguments, environmentOverrides,
|
|
index: index);
|
|
|
|
@override
|
|
SpecParseCommandOutput createOutput(
|
|
int exitCode,
|
|
bool timedOut,
|
|
List<int> stdout,
|
|
List<int> stderr,
|
|
Duration time,
|
|
bool compilationSkipped,
|
|
[int? pid = 0]) =>
|
|
SpecParseCommandOutput(
|
|
this, exitCode, timedOut, stdout, stderr, time, compilationSkipped);
|
|
}
|
|
|
|
class VMCommand extends ProcessCommand {
|
|
VMCommand(String executable, List<String> arguments,
|
|
Map<String, String> environmentOverrides,
|
|
{int index = 0})
|
|
: super('vm', executable, arguments, environmentOverrides, null, index);
|
|
|
|
@override
|
|
VMCommand indexedCopy(int index) =>
|
|
VMCommand(executable, arguments, environmentOverrides, index: index);
|
|
|
|
@override
|
|
CommandOutput createOutput(int exitCode, bool timedOut, List<int> stdout,
|
|
List<int> stderr, Duration time, bool compilationSkipped,
|
|
[int pid = 0]) =>
|
|
VMCommandOutput(this, exitCode, timedOut, stdout, stderr, time, pid);
|
|
}
|
|
|
|
// Run a VM test under RR, and copy the trace if it crashes. Using a helper
|
|
// script like the precompiler does not work because the RR traces are large
|
|
// and we must diligently erase them for non-crashes even if the test times
|
|
// out and would be killed by the harness, so the copying and cleanup logic
|
|
// must be in the harness.
|
|
class RRCommand extends Command {
|
|
ProcessCommand originalCommand;
|
|
late ProcessCommand wrappedCommand;
|
|
late io.Directory recordingDir;
|
|
late io.Directory savedDir;
|
|
|
|
RRCommand(this.originalCommand)
|
|
: super._(originalCommand.displayName, index: originalCommand.index) {
|
|
final suffix = "/rr-trace-${originalCommand.hashCode}";
|
|
recordingDir = io.Directory(io.Directory.systemTemp.path + suffix);
|
|
savedDir = io.Directory("out$suffix");
|
|
final executable = "rr";
|
|
final arguments = <String>[
|
|
"record",
|
|
"--chaos",
|
|
"--output-trace-dir=${recordingDir.path}",
|
|
];
|
|
arguments.add(originalCommand.executable);
|
|
arguments.addAll(originalCommand.nonBatchArguments);
|
|
arguments.addAll(originalCommand.arguments);
|
|
wrappedCommand = ProcessCommand(
|
|
originalCommand.displayName,
|
|
executable,
|
|
arguments,
|
|
originalCommand.environmentOverrides,
|
|
originalCommand.workingDirectory,
|
|
originalCommand.index);
|
|
}
|
|
|
|
@override
|
|
RRCommand indexedCopy(int index) =>
|
|
RRCommand(originalCommand.indexedCopy(index));
|
|
|
|
Future<CommandOutput> run(
|
|
int timeout, TestConfiguration configuration) async {
|
|
// rr will fail if the output trace directory already exists. Delete any
|
|
// that might be leftover from interrupting the harness.
|
|
if (await recordingDir.exists()) {
|
|
await recordingDir.delete(recursive: true);
|
|
}
|
|
final output = await RunningProcess(wrappedCommand, timeout,
|
|
configuration: configuration)
|
|
.run();
|
|
if (output.hasCrashed) {
|
|
if (await savedDir.exists()) {
|
|
await savedDir.delete(recursive: true);
|
|
}
|
|
await recordingDir.rename(savedDir.path);
|
|
await io.File("${savedDir.path}/command.txt")
|
|
.writeAsString(wrappedCommand.reproductionCommand);
|
|
await io.File("${savedDir.path}/stdout.txt").writeAsBytes(output.stdout);
|
|
await io.File("${savedDir.path}/stderr.txt").writeAsBytes(output.stderr);
|
|
} else if (await recordingDir.exists()) {
|
|
await recordingDir.delete(recursive: true);
|
|
}
|
|
|
|
final compilationSkipped = false;
|
|
switch (displayName) {
|
|
case 'app_jit':
|
|
case 'precompiler':
|
|
case 'run_vm_unittest':
|
|
case 'vm':
|
|
return VMCommandOutput(this, output.exitCode, output.hasTimedOut,
|
|
output.stdout, output.stderr, output.time, output.pid);
|
|
case 'dart2wasm':
|
|
return Dart2WasmCompilerCommandOutput(
|
|
this,
|
|
output.exitCode,
|
|
output.hasTimedOut,
|
|
output.stdout,
|
|
output.stderr,
|
|
output.time,
|
|
compilationSkipped);
|
|
case 'dart2js':
|
|
return Dart2jsCompilerCommandOutput(
|
|
this,
|
|
output.exitCode,
|
|
output.hasTimedOut,
|
|
output.stdout,
|
|
output.stderr,
|
|
output.time,
|
|
compilationSkipped);
|
|
case 'ddc':
|
|
return DevCompilerCommandOutput(
|
|
this,
|
|
output.exitCode,
|
|
output.hasTimedOut,
|
|
output.stdout,
|
|
output.stderr,
|
|
output.time,
|
|
compilationSkipped,
|
|
output.pid);
|
|
}
|
|
throw "Don't know how to interpret output for $displayName";
|
|
}
|
|
|
|
@override
|
|
String get reproductionCommand =>
|
|
"${wrappedCommand.reproductionCommand} (rr replay ${savedDir.path})";
|
|
|
|
@override
|
|
void _buildHashCode(HashCodeBuilder builder) {
|
|
originalCommand._buildHashCode(builder);
|
|
builder.add(42);
|
|
}
|
|
|
|
@override
|
|
bool _equal(RRCommand other) =>
|
|
hashCode == other.hashCode &&
|
|
originalCommand._equal(other.originalCommand);
|
|
}
|
|
|
|
abstract class AdbCommand {
|
|
String get buildPath;
|
|
List<String> get extraLibraries;
|
|
}
|
|
|
|
class AdbPrecompilationCommand extends Command implements AdbCommand {
|
|
@override
|
|
final String buildPath; // Path to the output directory of the build.
|
|
final String processTestFilename;
|
|
final String abstractSocketTestFilename;
|
|
final String precompiledTestDirectory;
|
|
final List<String> arguments;
|
|
final bool useElf;
|
|
@override
|
|
final List<String> extraLibraries;
|
|
|
|
AdbPrecompilationCommand(
|
|
this.buildPath,
|
|
this.processTestFilename,
|
|
this.abstractSocketTestFilename,
|
|
this.precompiledTestDirectory,
|
|
this.arguments,
|
|
this.useElf,
|
|
this.extraLibraries,
|
|
{super.index = 0})
|
|
: super._("adb_precompilation");
|
|
|
|
@override
|
|
AdbPrecompilationCommand indexedCopy(int index) => AdbPrecompilationCommand(
|
|
buildPath,
|
|
processTestFilename,
|
|
abstractSocketTestFilename,
|
|
precompiledTestDirectory,
|
|
arguments,
|
|
useElf,
|
|
extraLibraries,
|
|
index: index);
|
|
|
|
@override
|
|
VMCommandOutput createOutput(int exitCode, bool timedOut, List<int> stdout,
|
|
List<int> stderr, Duration time, bool compilationSkipped,
|
|
[int pid = 0]) =>
|
|
VMCommandOutput(this, exitCode, timedOut, stdout, stderr, time, pid);
|
|
|
|
@override
|
|
_buildHashCode(HashCodeBuilder builder) {
|
|
super._buildHashCode(builder);
|
|
builder.add(buildPath);
|
|
builder.add(precompiledTestDirectory);
|
|
builder.add(arguments);
|
|
builder.add(useElf);
|
|
extraLibraries.forEach(builder.add);
|
|
}
|
|
|
|
@override
|
|
bool _equal(AdbPrecompilationCommand other) =>
|
|
super._equal(other) &&
|
|
buildPath == other.buildPath &&
|
|
useElf == other.useElf &&
|
|
arguments == other.arguments &&
|
|
precompiledTestDirectory == other.precompiledTestDirectory &&
|
|
deepJsonCompare(extraLibraries, other.extraLibraries);
|
|
|
|
@override
|
|
String toString() => 'Steps to push precompiled runner and precompiled code '
|
|
'to an attached device. Uses (and requires) adb.';
|
|
|
|
@override
|
|
String get reproductionCommand => throw UnimplementedError();
|
|
}
|
|
|
|
class AdbDartkCommand extends Command implements AdbCommand {
|
|
@override
|
|
final String buildPath;
|
|
final String processTestFilename;
|
|
final String abstractSocketTestFilename;
|
|
final String kernelFile;
|
|
final List<String> arguments;
|
|
@override
|
|
final List<String> extraLibraries;
|
|
|
|
AdbDartkCommand(
|
|
this.buildPath,
|
|
this.processTestFilename,
|
|
this.abstractSocketTestFilename,
|
|
this.kernelFile,
|
|
this.arguments,
|
|
this.extraLibraries,
|
|
{super.index = 0})
|
|
: super._("adb_precompilation");
|
|
|
|
@override
|
|
AdbDartkCommand indexedCopy(int index) => AdbDartkCommand(
|
|
buildPath,
|
|
processTestFilename,
|
|
abstractSocketTestFilename,
|
|
kernelFile,
|
|
arguments,
|
|
extraLibraries,
|
|
index: index);
|
|
|
|
@override
|
|
_buildHashCode(HashCodeBuilder builder) {
|
|
super._buildHashCode(builder);
|
|
builder.add(buildPath);
|
|
builder.add(kernelFile);
|
|
builder.add(arguments);
|
|
builder.add(extraLibraries);
|
|
}
|
|
|
|
@override
|
|
bool _equal(AdbDartkCommand other) =>
|
|
super._equal(other) &&
|
|
buildPath == other.buildPath &&
|
|
arguments == other.arguments &&
|
|
extraLibraries == other.extraLibraries &&
|
|
kernelFile == other.kernelFile;
|
|
|
|
@override
|
|
VMCommandOutput createOutput(int exitCode, bool timedOut, List<int> stdout,
|
|
List<int> stderr, Duration time, bool compilationSkipped,
|
|
[int pid = 0]) =>
|
|
VMCommandOutput(this, exitCode, timedOut, stdout, stderr, time, pid);
|
|
|
|
@override
|
|
String toString() => 'Steps to push Dart VM and Dill file '
|
|
'to an attached device. Uses (and requires) adb.';
|
|
|
|
@override
|
|
String get reproductionCommand => throw UnimplementedError();
|
|
}
|
|
|
|
class JSCommandLineCommand extends ProcessCommand {
|
|
JSCommandLineCommand(
|
|
String displayName, String executable, List<String> arguments,
|
|
[Map<String, String> environmentOverrides = const {}, int index = 0])
|
|
: super(displayName, executable, arguments, environmentOverrides, null,
|
|
index);
|
|
|
|
@override
|
|
JSCommandLineCommand indexedCopy(int index) => JSCommandLineCommand(
|
|
displayName, executable, arguments, environmentOverrides, index);
|
|
|
|
@override
|
|
JSCommandLineOutput createOutput(
|
|
int exitCode,
|
|
bool timedOut,
|
|
List<int> stdout,
|
|
List<int> stderr,
|
|
Duration time,
|
|
bool compilationSkipped,
|
|
[int? pid = 0]) =>
|
|
JSCommandLineOutput(this, exitCode, timedOut, stdout, stderr, time);
|
|
}
|
|
|
|
class Dart2WasmCommandLineCommand extends ProcessCommand {
|
|
Dart2WasmCommandLineCommand(
|
|
String displayName, String executable, List<String> arguments,
|
|
[Map<String, String> environmentOverrides = const {}, int index = 0])
|
|
: super(displayName, executable, arguments, environmentOverrides, null,
|
|
index);
|
|
|
|
@override
|
|
Dart2WasmCommandLineCommand indexedCopy(int index) =>
|
|
Dart2WasmCommandLineCommand(
|
|
displayName, executable, arguments, environmentOverrides, index);
|
|
|
|
@override
|
|
Dart2WasmCommandLineOutput createOutput(
|
|
int exitCode,
|
|
bool timedOut,
|
|
List<int> stdout,
|
|
List<int> stderr,
|
|
Duration time,
|
|
bool compilationSkipped,
|
|
[int? pid = 0]) =>
|
|
Dart2WasmCommandLineOutput(
|
|
this, exitCode, timedOut, stdout, stderr, time);
|
|
}
|
|
|
|
/// [ScriptCommand]s are executed by dart code.
|
|
abstract class ScriptCommand extends Command {
|
|
ScriptCommand._(super.displayName) : super._();
|
|
|
|
Future<ScriptCommandOutput> run();
|
|
}
|