| // Copyright (c) 2024, 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. | |
| /// Utilities used by various parts of the test harness. | |
| library; | |
| import 'dart:convert'; | |
| import 'dart:io'; | |
| import 'dart:ffi' show Abi; | |
| /// Locate the root of the SDK repository. | |
| /// | |
| /// Note: we don't search for the directory "sdk" because this may not be | |
| /// available when running this test in a shard. | |
| Uri repoRoot = (() { | |
| var root = Platform.environment['REPO_ROOT']; | |
| if (root != null) return Uri.base.resolve(root); | |
| Uri script = Platform.script; | |
| var segments = script.pathSegments; | |
| var index = segments.lastIndexOf('pkg'); | |
| if (index == -1) { | |
| index = segments.lastIndexOf(_outFolder); // running from snapshot | |
| if (index == -1) { | |
| exitCode = 1; | |
| throw "error: cannot find the root of the Dart SDK"; | |
| } | |
| } | |
| return script.resolve("../" * (segments.length - index - 1)); | |
| })(); | |
| String _outFolder = Platform.isMacOS ? 'xcodebuild' : 'out'; | |
| String configuration = () { | |
| var env = Platform.environment['DART_CONFIGURATION']; | |
| if (env != null) return env; | |
| var folderSegments = dartBin.resolve('.').pathSegments; | |
| for (int i = folderSegments.length - 1; i > 0; i--) { | |
| if (folderSegments[i] == _outFolder) { | |
| var candidate = folderSegments[i + 1]; | |
| if (candidate.startsWith('Debug') || | |
| candidate.startsWith('Release') || | |
| candidate.startsWith('Product')) { | |
| return candidate; | |
| } | |
| } | |
| } | |
| return 'ReleaseX64'; | |
| }(); | |
| // See also utils/gen_kernel/BUILD.gn: | |
| // dartaotruntime has dart_product_config applied to it and is built in product | |
| // mode in both release and product builds. | |
| bool get useProduct => !configuration.startsWith('Debug'); | |
| String buildFolder = '$_outFolder/$configuration/'; | |
| String arch = Abi.current().toString().split('_')[1]; | |
| String _d8Path = (() { | |
| if (Platform.isWindows) { | |
| return 'third_party/d8/windows/$arch/d8.exe'; | |
| } else if (Platform.isLinux) { | |
| return 'third_party/d8/linux/$arch/d8'; | |
| } else if (Platform.isMacOS) { | |
| return 'third_party/d8/macos/$arch/d8'; | |
| } else { | |
| throw UnsupportedError( | |
| 'Unsupported platform for running d8: ' | |
| '${Platform.operatingSystem}', | |
| ); | |
| } | |
| })(); | |
| Uri d8Uri = repoRoot.resolve(_d8Path); | |
| Uri dartBin = Uri.file(Platform.resolvedExecutable); | |
| Uri dartAotBin = dartBin.resolve( | |
| Platform.isWindows ? 'dartaotruntime.exe' : 'dartaotruntime', | |
| ); | |
| Uri ddcAotSnapshot = dartBin.resolve('snapshots/dartdevc_aot.dart.snapshot'); | |
| Uri kernelWorkerAotSnapshot = dartBin.resolve( | |
| 'snapshots/kernel_worker_aot.dart.snapshot', | |
| ); | |
| Uri buildRootUri = repoRoot.resolve(buildFolder); | |
| Uri ddcSdkOutline = buildRootUri.resolve('ddc_outline.dill'); | |
| Uri ddcSdkJs = buildRootUri.resolve('gen/utils/ddc/stable/sdk/ddc/dart_sdk.js'); | |
| Uri ddcPreamblesJs = repoRoot.resolve( | |
| 'sdk/lib/_internal/js_dev_runtime/private/preambles/d8.js', | |
| ); | |
| Uri ddcSealNativeObjectJs = repoRoot.resolve( | |
| 'sdk/lib/_internal/js_runtime/lib/preambles/seal_native_object.js', | |
| ); | |
| Uri ddcModuleLoaderJs = repoRoot.resolve( | |
| 'pkg/dev_compiler/lib/js/ddc/ddc_module_loader.js', | |
| ); | |
| Uri genKernelSnapshot = buildRootUri.resolve( | |
| 'gen/gen_kernel_aot.dart.snapshot', | |
| ); | |
| Uri genSnapshotBin = buildRootUri.resolve( | |
| useProduct ? 'gen_snapshot_product' : 'gen_snapshot', | |
| ); | |
| Uri dart2bytecodeSnapshot = buildRootUri.resolve( | |
| 'gen/dart2bytecode.dart.snapshot', | |
| ); | |
| Uri aotRuntimeBin = buildRootUri.resolve( | |
| useProduct ? 'dartaotruntime_product' : 'dartaotruntime', | |
| ); | |
| Uri jitRuntimeBin = buildRootUri.resolve('dart'); | |
| Uri vmPlatformDill = buildRootUri.resolve('vm_platform.dill'); | |
| Uri dart2wasmSnapshot = dartBin.resolve('snapshots/dart2wasm_product.snapshot'); | |
| Uri dart2wasmPlatformDill = buildRootUri.resolve('dart2wasm_platform.dill'); | |
| Uri dart2wasmLibrariesSpec = repoRoot.resolve('sdk/lib/libraries.json'); | |
| Uri compileBenchmark = repoRoot.resolve('pkg/dart2wasm/tool/compile_benchmark'); | |
| Uri runBenchmark = repoRoot.resolve('pkg/dart2wasm/tool/run_benchmark'); | |
| // Encodes test results in the format expected by Dart's CI infrastructure. | |
| class TestResultOutcome { | |
| // This encoder must generate each output element on its own line. | |
| final _encoder = JsonEncoder(); | |
| final String configuration; | |
| final String suiteName; | |
| final String testName; | |
| late Duration elapsedTime; | |
| final String expectedResult; | |
| late bool matchedExpectations; | |
| String testOutput; | |
| TestResultOutcome({ | |
| required this.configuration, | |
| this.suiteName = 'dynamic_modules', | |
| required this.testName, | |
| this.expectedResult = 'Pass', | |
| this.testOutput = '', | |
| }); | |
| String toRecordJson() => _encoder.convert({ | |
| 'name': '$suiteName/$testName', | |
| 'configuration': configuration, | |
| 'suite': suiteName, | |
| 'test_name': testName, | |
| 'time_ms': elapsedTime.inMilliseconds, | |
| 'expected': expectedResult, | |
| 'result': matchedExpectations ? 'Pass' : 'Fail', | |
| 'matches': expectedResult == expectedResult, | |
| }); | |
| String toLogJson() => _encoder.convert({ | |
| 'name': '$suiteName/$testName', | |
| 'configuration': configuration, | |
| 'result': matchedExpectations ? 'Pass' : 'Fail', | |
| 'log': testOutput, | |
| }); | |
| } | |
| /// Runs [command] with [arguments] in [workingDirectory], and if [verbose] is | |
| /// `true` then it logs the full command. | |
| Future<ProcessResult> runProcess( | |
| String command, | |
| List<String> arguments, | |
| String workingDirectory, | |
| Logger logger, | |
| String message, | |
| ) async { | |
| logger.info( | |
| 'command:\n$command ${arguments.join(' ')} from $workingDirectory', | |
| ); | |
| final result = await Process.run( | |
| command, | |
| arguments, | |
| workingDirectory: workingDirectory, | |
| ); | |
| logger.info('Exit code: ${result.exitCode}'); | |
| if (result.exitCode != 0) { | |
| logger.warning('STDOUT: ${result.stdout}'); | |
| logger.warning('STDERR: ${result.stderr}'); | |
| throw 'Error on $message: $command ${arguments.join(' ')} from $workingDirectory\n\n' | |
| 'stdout:\n${result.stdout}\n\n' | |
| 'stderr:\n${result.stderr}'; | |
| } else { | |
| logger.info('STDOUT: ${result.stdout}'); | |
| logger.info('STDERR: ${result.stderr}'); | |
| } | |
| return result; | |
| } | |
| /// API to easily control verbosity of the test harness. | |
| class Logger { | |
| final bool verbose; | |
| Logger([this.verbose = false]); | |
| void info(String message) { | |
| if (verbose) print(message); | |
| } | |
| void warning(String message) => print(message); | |
| void error(String message) => print(message); | |
| } |