blob: de784ee233ab5861f20f601072f28fae4f030ab6 [file] [log] [blame]
// Copyright (c) 2014, 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:io';
import 'command.dart';
import 'compiler_configuration.dart';
import 'configuration.dart';
import 'fuchsia.dart';
import 'repository.dart';
import 'utils.dart';
/// Describes the commands to run a given test case or its compiled output.
///
/// A single runtime configuration object exists per test suite, and is thus
/// shared between multiple test cases, it should not be mutated after
/// construction.
abstract class RuntimeConfiguration {
late TestConfiguration _configuration;
static RuntimeConfiguration _makeInstance(TestConfiguration configuration) {
switch (configuration.runtime) {
case Runtime.chrome:
case Runtime.chromeOnAndroid:
case Runtime.firefox:
case Runtime.ie11:
case Runtime.ie10:
case Runtime.ie9:
case Runtime.safari:
// TODO(ahe): Replace this with one or more browser runtimes.
return DummyRuntimeConfiguration();
case Runtime.jsc:
return JSCRuntimeConfiguration(configuration.compiler);
case Runtime.jsshell:
return JsshellRuntimeConfiguration(configuration.compiler);
case Runtime.d8:
return D8RuntimeConfiguration(configuration.compiler);
case Runtime.none:
return NoneRuntimeConfiguration();
case Runtime.vm:
if (configuration.system == System.android) {
return DartkAdbRuntimeConfiguration();
} else if (configuration.system == System.fuchsia) {
return DartkFuchsiaEmulatorRuntimeConfiguration();
}
return StandaloneDartRuntimeConfiguration();
case Runtime.dartPrecompiled:
if (configuration.system == System.android) {
return DartPrecompiledAdbRuntimeConfiguration(
configuration.useElf,
);
} else {
return DartPrecompiledRuntimeConfiguration(
configuration.useElf,
);
}
}
throw "unreachable";
}
factory RuntimeConfiguration(TestConfiguration configuration) {
return _makeInstance(configuration).._configuration = configuration;
}
RuntimeConfiguration._subclass();
int timeoutMultiplier(
{required Mode mode,
bool isChecked = false,
bool isReload = false,
required Architecture arch,
required System system}) {
return 1;
}
List<Command> computeRuntimeCommands(
CommandArtifact? artifact,
List<String> arguments,
Map<String, String> environmentOverrides,
List<String> extraLibs,
bool isCrashExpected) {
// TODO(ahe): Make this method abstract.
throw "Unimplemented runtime '$runtimeType'";
}
/// The output directory for this suite's configuration.
String get buildDir => _configuration.buildDirectory;
List<String> dart2jsPreambles(Uri preambleDir) => [];
/// Returns the path to the Dart VM executable.
///
/// Controlled by user with the option "--dart".
String get dartVmBinaryFileName =>
_configuration.dartPath ?? dartVmExecutableFileName;
String get dartVmExecutableFileName {
return _configuration.useSdk
? '$buildDir/dart-sdk/bin/dart$executableExtension'
: '$buildDir/dart$executableExtension';
}
String get dartPrecompiledBinaryFileName {
// Controlled by user with the option "--dart-precompiled".
var dartExecutable = _configuration.dartPrecompiledPath;
if (dartExecutable == null || dartExecutable == '') {
var dir = buildDir;
// gen_snapshot can run with different word sizes, but the simulators
// cannot.
dir = dir.replaceAll("SIMARM_X64", "SIMARM");
dartExecutable = '$dir/dart_precompiled_runtime$executableExtension';
}
TestUtils.ensureExists(dartExecutable, _configuration);
return dartExecutable;
}
String get processTestBinaryFileName {
var processTestExecutable = '$buildDir/process_test$executableExtension';
TestUtils.ensureExists(processTestExecutable, _configuration);
return processTestExecutable;
}
String get abstractSocketTestBinaryFileName {
var abstractSocketTestExecutable =
'$buildDir/abstract_socket_test$executableExtension';
TestUtils.ensureExists(abstractSocketTestExecutable, _configuration);
return abstractSocketTestExecutable;
}
String get d8FileName {
var d8Dir = Repository.dir.append('third_party/d8');
var d8Path = d8Dir.append(
'${Platform.operatingSystem}/${Architecture.host}/d8$executableExtension');
var d8 = d8Path.toNativePath();
TestUtils.ensureExists(d8, _configuration);
return d8;
}
String get jscFileName {
final jscPath =
Repository.dir.append('third_party/jsc/jsc$executableExtension');
final jsc = jscPath.toNativePath();
TestUtils.ensureExists(jsc, _configuration);
return jsc;
}
String get jsShellFileName {
var executable = 'jsshell$executableExtension';
var jsshellDir = Repository.uri.resolve("tools/testing/bin").path;
var jsshell = '$jsshellDir/$executable';
TestUtils.ensureExists(jsshell, _configuration);
return jsshell;
}
}
/// The 'none' runtime configuration.
class NoneRuntimeConfiguration extends RuntimeConfiguration {
NoneRuntimeConfiguration() : super._subclass();
@override
List<Command> computeRuntimeCommands(
CommandArtifact? artifact,
List<String> arguments,
Map<String, String> environmentOverrides,
List<String> extraLibs,
bool isCrashExpected) {
return <Command>[];
}
}
class CommandLineJavaScriptRuntime extends RuntimeConfiguration {
final String moniker;
CommandLineJavaScriptRuntime(this.moniker) : super._subclass();
void checkArtifact(CommandArtifact artifact) {
var type = artifact.mimeType;
if (type != 'application/javascript' && type != 'application/wasm') {
throw "Runtime '$moniker' cannot run files of type '$type'.";
}
}
}
/// Chrome/V8-based development shell (d8).
class D8RuntimeConfiguration extends CommandLineJavaScriptRuntime {
final Compiler compiler;
D8RuntimeConfiguration(this.compiler) : super('d8');
@override
List<Command> computeRuntimeCommands(
CommandArtifact? artifact,
List<String> arguments,
Map<String, String> environmentOverrides,
List<String> extraLibs,
bool isCrashExpected) {
// TODO(ahe): Avoid duplication of this method between d8 and jsshell.
checkArtifact(artifact!);
if (compiler == Compiler.dart2wasm) {
return [
Dart2WasmCommandLineCommand(
moniker, d8FileName, arguments, environmentOverrides)
];
} else {
return [
JSCommandLineCommand(
moniker, d8FileName, arguments, environmentOverrides)
];
}
}
@override
List<String> dart2jsPreambles(Uri preambleDir) {
return [
preambleDir.resolve('seal_native_object.js').toFilePath(),
preambleDir.resolve('d8.js').toFilePath()
];
}
}
/// Safari/WebKit/JavaScriptCore-based development shell (jsc).
class JSCRuntimeConfiguration extends CommandLineJavaScriptRuntime {
final Compiler compiler;
JSCRuntimeConfiguration(this.compiler) : super('jsc');
@override
List<Command> computeRuntimeCommands(
CommandArtifact? artifact,
List<String> arguments,
Map<String, String> environmentOverrides,
List<String> extraLibs,
bool isCrashExpected) {
checkArtifact(artifact!);
if (compiler != Compiler.dart2wasm) {
throw 'No test runner setup for jsc + dart2js yet';
}
final environment = {
...environmentOverrides,
'JSC_useWebAssemblyTypedFunctionReferences': '1',
'JSC_useWebAssemblyExtendedConstantExpression': '1',
'JSC_useWebAssemblyGC': '1',
};
return [
Dart2WasmCommandLineCommand(moniker, jscFileName, arguments, environment)
];
}
}
/// Firefox/SpiderMonkey-based development shell (jsshell).
class JsshellRuntimeConfiguration extends CommandLineJavaScriptRuntime {
final Compiler compiler;
JsshellRuntimeConfiguration(this.compiler) : super('jsshell');
@override
List<Command> computeRuntimeCommands(
CommandArtifact? artifact,
List<String> arguments,
Map<String, String> environmentOverrides,
List<String> extraLibs,
bool isCrashExpected) {
checkArtifact(artifact!);
if (compiler == Compiler.dart2wasm) {
return [
Dart2WasmCommandLineCommand(
moniker, jsShellFileName, arguments, environmentOverrides)
];
} else {
return [
JSCommandLineCommand(
moniker, jsShellFileName, arguments, environmentOverrides)
];
}
}
@override
List<String> dart2jsPreambles(Uri preambleDir) {
return [
'-f',
preambleDir.resolve('seal_native_object.js').toFilePath(),
'-f',
preambleDir.resolve('jsshell.js').toFilePath(),
'-f'
];
}
}
class QemuConfig {
static const all = <Architecture, QemuConfig>{
Architecture.ia32:
QemuConfig('qemu-i386', ['-L', '/usr/lib/i386-linux-gnu/']),
Architecture.x64:
QemuConfig('qemu-x86_64', ['-L', '/usr/lib/x86_64-linux-gnu/']),
Architecture.x64c:
QemuConfig('qemu-x86_64', ['-L', '/usr/lib/x86_64-linux-gnu/']),
Architecture.arm:
QemuConfig('qemu-arm', ['-L', '/usr/arm-linux-gnueabihf/']),
Architecture.arm64:
QemuConfig('qemu-aarch64', ['-L', '/usr/aarch64-linux-gnu/']),
Architecture.arm64c:
QemuConfig('qemu-aarch64', ['-L', '/usr/aarch64-linux-gnu/']),
Architecture.riscv32:
QemuConfig('qemu-riscv32', ['-L', '/usr/riscv32-linux-gnu/']),
Architecture.riscv64:
QemuConfig('qemu-riscv64', ['-L', '/usr/riscv64-linux-gnu/']),
};
final String executable;
final List<String> arguments;
const QemuConfig(this.executable, this.arguments);
}
/// Common runtime configuration for runtimes based on the Dart VM.
class DartVmRuntimeConfiguration extends RuntimeConfiguration {
DartVmRuntimeConfiguration() : super._subclass();
@override
int timeoutMultiplier(
{required Mode mode,
bool isChecked = false,
bool isReload = false,
required Architecture arch,
required System system}) {
var multiplier = 1;
switch (arch) {
case Architecture.simarm:
case Architecture.simarm64:
case Architecture.simarm64c:
case Architecture.simriscv32:
case Architecture.simriscv64:
multiplier *= 4;
break;
}
if (_configuration.useQemu) {
multiplier *= 2;
}
// Configurations where `kernel-service` doesn't run from AppJIT snapshot
// will make tests run very slow due to the `kernel-service` code slowly
// warming up the JIT. This is especially noticable in `debug` mode.
if (arch == Architecture.ia32) {
multiplier *= 2;
}
if (mode.isDebug) {
multiplier *= 2;
}
if (isReload) {
multiplier *= 2;
}
if (_configuration.sanitizer != Sanitizer.none) {
multiplier *= 4;
}
if (_configuration.rr) {
multiplier *= 2;
}
return multiplier;
}
}
//// The standalone Dart VM binary, "dart" or "dart.exe".
class StandaloneDartRuntimeConfiguration extends DartVmRuntimeConfiguration {
@override
List<Command> computeRuntimeCommands(
CommandArtifact? artifact,
List<String> arguments,
Map<String, String> environmentOverrides,
List<String> extraLibs,
bool isCrashExpected) {
var script = artifact?.filename;
var type = artifact?.mimeType;
if (script != null &&
type != 'application/dart' &&
type != 'application/dart-snapshot' &&
type != 'application/kernel-ir' &&
type != 'application/kernel-ir-fully-linked') {
throw "Dart VM cannot run files of type '$type'.";
}
if (isCrashExpected) {
arguments.insert(0, '--suppress-core-dump');
}
var executable = dartVmBinaryFileName;
if (type == 'application/kernel-ir-fully-linked') {
executable = dartVmExecutableFileName;
}
if (_configuration.useQemu) {
final config = QemuConfig.all[_configuration.architecture]!;
arguments.insert(0, executable);
arguments.insertAll(0, config.arguments);
executable = config.executable;
}
var command = VMCommand(executable, arguments, environmentOverrides);
if (_configuration.rr && !isCrashExpected) {
return [RRCommand(command)];
}
return [command];
}
}
class DartPrecompiledRuntimeConfiguration extends DartVmRuntimeConfiguration {
final bool useElf;
DartPrecompiledRuntimeConfiguration(this.useElf);
@override
List<Command> computeRuntimeCommands(
CommandArtifact? artifact,
List<String> arguments,
Map<String, String> environmentOverrides,
List<String> extraLibs,
bool isCrashExpected) {
var script = artifact?.filename;
var type = artifact?.mimeType;
if (script != null && type != 'application/dart-precompiled') {
throw "dart_precompiled cannot run files of type '$type'.";
}
var executable = dartPrecompiledBinaryFileName;
if (_configuration.useQemu) {
final config = QemuConfig.all[_configuration.architecture]!;
arguments.insert(0, executable);
arguments.insertAll(0, config.arguments);
executable = config.executable;
}
var command = VMCommand(executable, arguments, environmentOverrides);
if (_configuration.rr && !isCrashExpected) {
return [RRCommand(command)];
}
return [command];
}
}
class DartkAdbRuntimeConfiguration extends DartVmRuntimeConfiguration {
static const String deviceDir = '/data/local/tmp/testing';
static const String deviceTestDir = '/data/local/tmp/testing/test';
@override
List<Command> computeRuntimeCommands(
CommandArtifact? artifact,
List<String> arguments,
Map<String, String> environmentOverrides,
List<String> extraLibs,
bool isCrashExpected) {
var script = artifact?.filename;
var type = artifact?.mimeType;
if (script != null && type != 'application/kernel-ir-fully-linked') {
throw "dart cannot run files of type '$type'.";
}
var buildPath = buildDir;
var processTest = processTestBinaryFileName;
var abstractSocketTest = abstractSocketTestBinaryFileName;
return [
AdbDartkCommand(buildPath, processTest, abstractSocketTest, script!,
arguments, extraLibs)
];
}
}
class DartPrecompiledAdbRuntimeConfiguration
extends DartVmRuntimeConfiguration {
static const deviceDir = '/data/local/tmp/precompilation-testing';
static const deviceTestDir = '/data/local/tmp/precompilation-testing/test';
final bool useElf;
DartPrecompiledAdbRuntimeConfiguration(this.useElf);
@override
List<Command> computeRuntimeCommands(
CommandArtifact? artifact,
List<String> arguments,
Map<String, String> environmentOverrides,
List<String> extraLibs,
bool isCrashExpected) {
var script = artifact?.filename;
var type = artifact?.mimeType;
if (script != null && type != 'application/dart-precompiled') {
throw "dart_precompiled cannot run files of type '$type'.";
}
var processTest = processTestBinaryFileName;
var abstractSocketTest = abstractSocketTestBinaryFileName;
return [
AdbPrecompilationCommand(buildDir, processTest, abstractSocketTest,
script!, arguments, useElf, extraLibs)
];
}
}
class DartkFuchsiaEmulatorRuntimeConfiguration
extends DartVmRuntimeConfiguration {
@override
List<Command> computeRuntimeCommands(
CommandArtifact? artifact,
List<String> arguments,
Map<String, String> environmentOverrides,
List<String> extraLibs,
bool isCrashExpected) {
var script = artifact?.filename;
var type = artifact?.mimeType;
if (script != null &&
type != 'application/dart' &&
type != 'application/dart-snapshot' &&
type != 'application/kernel-ir' &&
type != 'application/kernel-ir-fully-linked') {
throw "Dart VM cannot run files of type '$type'.";
}
if (isCrashExpected) {
arguments.insert(0, '--suppress-core-dump');
}
// Rewrite paths on the host to paths in the Fuchsia package.
arguments = arguments
.map((argument) =>
argument.replaceAll(Directory.current.path, "pkg/data"))
.toList();
var command = FuchsiaEmulator.instance().getTestCommand(
_configuration.buildDirectory,
_configuration.mode.name,
_configuration.architecture.name,
arguments);
command.arguments
.insert(command.arguments.length - 1, '--disable-dart-dev');
command.environmentOverrides.addAll(environmentOverrides);
print("+ About to run command $command to test against fuchsia vm");
return [command];
}
}
/// Temporary runtime configuration for browser runtimes that haven't been
/// migrated yet.
// TODO(ahe): Remove this class.
class DummyRuntimeConfiguration extends DartVmRuntimeConfiguration {
@override
List<Command> computeRuntimeCommands(
CommandArtifact? artifact,
List<String> arguments,
Map<String, String> environmentOverrides,
List<String> extraLibs,
bool isCrashExpected) {
throw "Unimplemented runtime '$runtimeType'";
}
}