blob: 952bc4e708a32701555bd642605311f9c3b1570f [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 ddcSnapshot = _dartBin.resolve('snapshots/dartdevc.dart.snapshot');
Uri kernelWOrkerSnapshot =
_dartBin.resolve('snapshots/kernel_worker.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
? 'dart_precompiled_runtime_product'
: 'dart_precompiled_runtime');
Uri vmPlatformDill = buildRootUri.resolve('vm_platform_strong.dill');
// 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);
}