blob: 097a0490ed3185be69d1926a591bfad87c8089e2 [file] [log] [blame]
// 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.
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:expect/expect.dart';
import 'package:path/path.dart' as p;
import 'package:test/test.dart';
final dartSdkOutDirectory = _findSdkOutDirectory();
String _findSdkOutDirectory() {
var dartSdkOutDirectory = File(Platform.resolvedExecutable).parent;
if (!Directory.fromUri(dartSdkOutDirectory.uri.resolve('gen')).existsSync()) {
// Not next to `dart`, try two levels up for `dart-sdk/bin` suffix.
dartSdkOutDirectory = dartSdkOutDirectory.parent.parent;
}
if (!Directory.fromUri(dartSdkOutDirectory.uri.resolve('gen')).existsSync()) {
fail("Can't find SDK 'gen' directory from ${Platform.resolvedExecutable}, "
'please run from an SDK build out.');
}
return dartSdkOutDirectory.path;
}
/// Tests a macro build specified by [commands].
///
/// The commands are launched with current directory set to a temp folder with
/// a fresh copy of the package `package_under_test`.
///
/// The commands should build and run `bin/main.dart`. It is a test that will
/// string `OK\n` showing that the macro output ran.
///
/// In commands, the string `$DART` is replaced to refer to the `dart` command
/// in the Dart SDK under test; and `$DART_SDK` to the root of the built Dart
/// SDK under test.
///
/// The test passes if all commands return exit code 0.
Future<void> testMacroBuild(List<String> commands) async {
var temp = Directory.systemTemp.createTempSync('macro_build_test');
var sourceDirectory =
Directory.current.path + '/tests/macro_build/package_under_test';
var workingDirectory = '${temp.path}/package_under_test';
await _copyPath(sourceDirectory, workingDirectory);
final dartSdkPath = Directory.current.path;
final dartPath = Platform.resolvedExecutable;
// TODO(davidmorgan): run on more platforms.
final configuration = String.fromEnvironment('test_runner.configuration');
if (configuration.isEmpty) {
print(r'''
Hint: this test is an e2e test of SDK tools, consider running using the test
runner to ensure they are built and not stale:
./tools/test.py -v -nunittest-asserts-release-linux-x64 \
--build 'tests/macro_build/*'
''');
}
_fixPubspec(
pubspecPath: '$workingDirectory/pubspec.yaml', dartSdkPath: dartSdkPath);
var failed = false;
var timedOut = false;
for (var command in commands) {
if (command.contains(r'$DART_SDK_OUT')) {
// Only search for SDK out directory if it's needed for this test case.
command = command.replaceAll(r'$DART_SDK_OUT', dartSdkOutDirectory);
}
final commandParts = command
.replaceAll(r'$DART_SDK', dartSdkPath)
.replaceAll(r'$DART', dartPath)
.split(' ')
.map((c) => c.replaceAll(r'$SPACE', ' '))
.toList();
print('Running: ${commandParts.join(' ')}');
final process = await Process.start(
commandParts.first, commandParts.skip(1).toList(),
workingDirectory: workingDirectory);
try {
final result = await process.exitCode.timeout(Duration(seconds: 60));
if (result != 0) {
failed = true;
}
} on TimeoutException catch (_) {
timedOut = true;
process.kill();
}
final stdout =
(await process.stdout.transform(utf8.decoder).toList()).join('');
final stderr =
(await process.stderr.transform(utf8.decoder).toList()).join('');
print('--- stdout ---\n$stdout--- stderr ---\n$stderr---\n');
if (timedOut || failed) break;
}
if (failed) {
Expect.fail('Command exited with non-zero exit code.');
}
if (timedOut) {
Expect.fail('Command ran for more than 60s.');
}
}
Future<void> _copyPath(String from, String to) async {
await Directory(to).create(recursive: true);
await for (final file in Directory(from).list(recursive: true)) {
final copyTo = p.join(to, p.relative(file.path, from: from));
if (file is Directory) {
await Directory(copyTo).create(recursive: true);
} else if (file is File) {
await File(file.path).copy(copyTo);
}
}
}
/// Fixes relative paths in the pubspec at [pubspecPath] to refer to the
/// Dart SDK path [dartSdkPath].
void _fixPubspec({required String pubspecPath, required String dartSdkPath}) {
print('Updated $pubspecPath to point to SDK under $dartSdkPath.');
var file = File(pubspecPath);
file.writeAsStringSync(
file.readAsStringSync().replaceAll('../../..', dartSdkPath));
}