// Copyright (c) 2020, 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:io'; import 'package:expect/expect.dart'; import 'package:path/path.dart' as path; final executable = Platform.executable; main() async { // Run the Dart VM with or without: // --packages= for (final runWithPackagesArg in const [true, false]) { // Run the isolate with or without // Isolate.spawnUri(..., packageConfig: ) print('TEST runWithPackagesArg = $runWithPackagesArg '); for (final spawnWithPackageConfig in const [true, false]) { print('TEST spawnWithPackageConfig = $spawnWithPackageConfig '); final bool checkForResolveUri = runWithPackagesArg || !spawnWithPackageConfig; await runDotPackagesTest( runWithPackagesArg, spawnWithPackageConfig, checkForResolveUri); for (final optionalPackageUri in const [true, false]) { print('TEST optionalPackageUri = $optionalPackageUri'); await runPackageConfigTest(runWithPackagesArg, spawnWithPackageConfig, optionalPackageUri, checkForResolveUri); } } } } Future runPackageConfigTest(bool withPackagesArg, bool spawnWithArg, bool optionalPackageUri, bool checkForResolveUri) async { await withApplicationDirAndDotDartToolPackageConfig( (String tempDir, String packageJson, String mainFile) async { final args = [if (withPackagesArg) '--packages=$packageJson', mainFile]; await run(executable, args); }, spawnWithArg, optionalPackageUri, checkForResolveUri); } Future runDotPackagesTest( bool withPackagesArg, bool spawnWithArg, bool checkForResolveUri) async { await withApplicationDirAndDotPackages( (String tempDir, String dotPackagesFile, String mainFile) async { final args = [ if (withPackagesArg) '--packages=$dotPackagesFile', mainFile, ]; await run(executable, args); }, spawnWithArg, checkForResolveUri); } Future withApplicationDirAndDotPackages( Future fn(String tempDir, String packagesDir, String mainFile), bool spawnWithArg, bool checkForResolveUri) async { await withTempDir((String tempDir) async { // Setup ".packages" final dotPackagesFile = path.join(tempDir, spawnWithArg ? 'baz.packages' : '.packages'); await File(dotPackagesFile).writeAsString(buildDotPackages('foo')); final mainFile = path.join(tempDir, 'main.dart'); final childIsolateFile = path.join(tempDir, 'child_isolate.dart'); final importUri = 'package:foo/child_isolate.dart'; await File(childIsolateFile).writeAsString(buildChildIsolate()); await File(mainFile).writeAsString(buildMainIsolate( importUri, spawnWithArg ? dotPackagesFile : null, checkForResolveUri ? childIsolateFile : null)); await fn(tempDir, dotPackagesFile, mainFile); }); } Future withApplicationDirAndDotDartToolPackageConfig( Future fn(String tempDir, String packageJson, String mainFile), bool spawnWithArg, bool optionalPackageUri, bool checkForResolveUri) async { await withTempDir((String tempDir) async { // Setup ".dart_tool/package_config.json" final dotDartToolDir = path.join(tempDir, '.dart_tool'); await Directory(dotDartToolDir).create(); final packageConfigJsonFile = path.join( dotDartToolDir, spawnWithArg ? 'baz.packages' : 'package_config.json'); await File(packageConfigJsonFile) .writeAsString(buildPackageConfig('foo', optionalPackageUri)); // Setup actual application final mainFile = path.join(tempDir, 'main.dart'); final childIsolateFile = path.join(tempDir, 'child_isolate.dart'); final importUri = 'package:foo/child_isolate.dart'; await File(childIsolateFile).writeAsString(buildChildIsolate()); await File(mainFile).writeAsString(buildMainIsolate( importUri, spawnWithArg ? packageConfigJsonFile : null, checkForResolveUri ? childIsolateFile : null)); await fn(tempDir, packageConfigJsonFile, mainFile); }); } Future withTempDir(Future fn(String dir)) async { final dir = await Directory.systemTemp.createTemp('spawn_uri'); try { await fn(dir.absolute.path); } finally { await dir.delete(recursive: true); } } Future run(String executable, List args, {String cwd}) async { print('Running $executable ${args.join(' ')}'); final String workingDirectory = cwd ?? Directory.current.absolute.path; final result = await Process.run(executable, ['--trace-loading', ...args], workingDirectory: workingDirectory); print('exitCode:\n${result.exitCode}'); print('stdout:\n${result.stdout}'); print('stdout:\n${result.stderr}'); Expect.equals(0, result.exitCode); return result; } String buildDotPackages(String packageName) => '$packageName:.'; String buildPackageConfig(String packageName, bool optionalPackageUri) => ''' { "configVersion": 2, "packages": [ { "name": "$packageName", "rootUri": "../" ${optionalPackageUri ? ', "packageUri": "./"' : ''} } ] } '''; String buildChildIsolate() => ''' import 'dart:isolate'; main(List args, SendPort message) { message.send('child isolate is done'); } '''; String buildMainIsolate( String spawnUri, String packageConfigUri, String childIsolatePath) => ''' import 'dart:isolate'; import 'dart:io' as io; main(List args) async { io.exitCode = 1; final uri = Uri.parse('$spawnUri'); final resolvedUri = await Isolate.resolvePackageUri(uri); if ("""\${resolvedUri?.toFilePath()}""" != r"""$childIsolatePath""") { throw 'Could not Isolate.resolvePackageUri(uri).'; } final rp = ReceivePort(); final isolateArgs = ['a']; await Isolate.spawnUri( uri, isolateArgs, rp.sendPort, packageConfig: ${packageConfigUri != null ? 'Uri.file(r"$packageConfigUri")' : 'null'}); final childIsolateMessage = await rp.first; if (childIsolateMessage != 'child isolate is done') { throw 'Did not receive correct message from child isolate.'; } // Test was successful. io.exitCode = 0; } ''';