// 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 = (() { Uri script = Platform.script; var segments = script.pathSegments; var index = segments.lastIndexOf('pkg'); 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 vmPlatformDill = buildRootUri.resolve('vm_platform_strong.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 runProcess( String command, List 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); }