| // 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)); | 
 | } |