blob: cfe7384eecf7c0b25206bf444fba5f981ed28576 [file] [log] [blame] [edit]
// 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.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);
}