| // 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.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(false); |
| } |
| return StandaloneDartRuntimeConfiguration(); |
| |
| case Runtime.dartPrecompiled: |
| if (configuration.system == System.android) { |
| return DartPrecompiledAdbRuntimeConfiguration( |
| configuration.genSnapshotFormat == GenSnapshotFormat.elf, |
| ); |
| } else if (configuration.system == System.fuchsia) { |
| return DartkFuchsiaEmulatorRuntimeConfiguration(true); |
| } |
| return DartPrecompiledRuntimeConfiguration( |
| configuration.genSnapshotFormat == GenSnapshotFormat.elf, |
| ); |
| } |
| 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/dartaotruntime$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, 'pkg/dart2wasm/tool/run_benchmark', |
| ['--d8', ...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'; |
| } |
| // TODO(https://bugs.webkit.org/show_bug.cgi?id=285902): Once the |
| // JavaScriptCore/WebKit bug is fixed we should be able to remove this |
| // again. |
| const jscBugWorkaround = '--shell-option=--useWasmIPInt=false'; |
| return [ |
| Dart2WasmCommandLineCommand(moniker, 'pkg/dart2wasm/tool/run_benchmark', |
| ['--jsc', jscBugWorkaround, ...arguments], environmentOverrides) |
| ]; |
| } |
| } |
| |
| /// 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, 'pkg/dart2wasm/tool/run_benchmark', |
| ['--jsshell', ...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', '/usr/lib/i386-linux-gnu/'), |
| Architecture.x64: |
| QemuConfig('qemu-x86_64', '/usr/lib/x86_64-linux-gnu/'), |
| Architecture.x64c: |
| QemuConfig('qemu-x86_64', '/usr/lib/x86_64-linux-gnu/'), |
| Architecture.arm: |
| QemuConfig('qemu-arm', '/usr/arm-linux-gnueabihf/'), |
| Architecture.arm64: |
| QemuConfig('qemu-aarch64', '/usr/aarch64-linux-gnu/'), |
| Architecture.simarm64_arm64: |
| QemuConfig('qemu-aarch64', '/usr/aarch64-linux-gnu/'), |
| Architecture.arm64c: |
| QemuConfig('qemu-aarch64', '/usr/aarch64-linux-gnu/'), |
| Architecture.riscv32: |
| QemuConfig('qemu-riscv32', '/usr/riscv32-linux-gnu/'), |
| Architecture.riscv64: |
| QemuConfig('qemu-riscv64', '/usr/riscv64-linux-gnu/'), |
| }; |
| |
| final String executable; |
| final String elfInterpreterPrefix; |
| |
| const QemuConfig(this.executable, this.elfInterpreterPrefix); |
| } |
| |
| /// 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; |
| } |
| if (system == System.fuchsia && arch == Architecture.arm64) { |
| multiplier *= 4; // Full system QEMU. |
| } |
| |
| // 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 ((arch == Architecture.x64 && system == System.mac) || |
| (arch == Architecture.arm64 && system == System.win)) { |
| multiplier *= 2; // Slower machines. |
| } |
| |
| 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' && |
| type != 'application/dart-bytecode') { |
| 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); |
| executable = config.executable; |
| if (environmentOverrides['QEMU_LD_PREFIX'] == null) { |
| environmentOverrides['QEMU_LD_PREFIX'] = config.elfInterpreterPrefix; |
| } |
| } |
| 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' && |
| type != 'application/dart-bytecode') { |
| throw "dartaotruntime cannot run files of type '$type'."; |
| } |
| |
| var executable = dartPrecompiledBinaryFileName; |
| |
| if (_configuration.useQemu) { |
| final config = QemuConfig.all[_configuration.architecture]!; |
| arguments.insert(0, executable); |
| executable = config.executable; |
| if (environmentOverrides['QEMU_LD_PREFIX'] == null) { |
| environmentOverrides['QEMU_LD_PREFIX'] = config.elfInterpreterPrefix; |
| } |
| } |
| |
| 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 "dartaotruntime 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 { |
| final bool aot; |
| DartkFuchsiaEmulatorRuntimeConfiguration(this.aot); |
| |
| @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 component = "dartvm_test_component.cm"; |
| if (aot) { |
| component = "dartaotruntime_test_component.cm"; |
| arguments[arguments.length - 1] = |
| arguments[arguments.length - 1].replaceAll(".dart", ".dart.elf"); |
| } |
| |
| arguments.insert(arguments.length - 1, '--disable-dart-dev'); |
| return [ |
| FuchsiaEmulator.instance().getTestCommand( |
| _configuration.buildDirectory, |
| _configuration.mode.name, |
| _configuration.architecture.name, |
| component, |
| arguments, |
| environmentOverrides) |
| ]; |
| } |
| } |
| |
| /// 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'"; |
| } |
| } |