| // 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'"; |
| } |
| } |