From 4fddaf9486c4680ea703eef94792b421db50a1ce Mon Sep 17 00:00:00 2001 From: Vyacheslav Egorov Date: Tue, 11 Jul 2023 12:59:33 +0000 Subject: [PATCH] [sdk] Provide Isolate.resolvePackageUriSync TEST=augmented few existing tests Bug: https://github.com/dart-lang/sdk/issues/52121 CoreLibraryReviewExempt: VM-only change, other platforms don't support this API. Change-Id: I95decae6cf1a5c6ad694747313aa0dbe0a13025d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/312981 Reviewed-by: Lasse Nielsen Commit-Queue: Slava Egorov Reviewed-by: Martin Kustermann --- CHANGELOG.md | 4 + .../js_dev_runtime/patch/isolate_patch.dart | 6 + .../js_runtime/lib/isolate_patch.dart | 10 + sdk/lib/_internal/vm/bin/builtin.dart | 14 +- sdk/lib/_internal/vm/lib/internal_patch.dart | 4 +- sdk/lib/_internal/vm/lib/isolate_patch.dart | 31 +- sdk/lib/_internal/wasm/lib/isolate_patch.dart | 10 + sdk/lib/isolate/isolate.dart | 89 +- tests/lib/isolate/package_resolve_test.dart | 11 +- .../lib/isolate/resolve_package_uri_test.dart | 13 - .../package_resolve_test.dart | 14 +- .../bad_resolve_package_test.dart | 19 +- tests/lib/lib.status | 1 - tests/lib_2/isolate/package_resolve_test.dart | 6 + .../isolate/resolve_package_uri_test.dart | 15 - .../package_resolve_test.dart | 14 +- .../bad_resolve_package_test.dart | 6 + tests/lib_2/lib_2.status | 1 - tests/standalone/packages_file_test.dart | 1007 ---------------- tests/standalone/standalone.status | 2 - tests/standalone_2/packages_file_test.dart | 1009 ----------------- tests/standalone_2/standalone_2.status | 2 - 22 files changed, 185 insertions(+), 2103 deletions(-) delete mode 100644 tests/lib/isolate/resolve_package_uri_test.dart delete mode 100644 tests/lib_2/isolate/resolve_package_uri_test.dart delete mode 100644 tests/standalone/packages_file_test.dart delete mode 100644 tests/standalone_2/packages_file_test.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 522154c9247..3e0daa0f2df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,10 @@ [#51486]: https://github.com/dart-lang/sdk/issues/51486 [#52027]: https://github.com/dart-lang/sdk/issues/52027 +#### `dart:isolate` + +- Added `Isolate.packageConfigSync` and `Isolate.resolvePackageUriSync` APIs. + #### `dart:js_interop` - **Object literal constructors**: diff --git a/sdk/lib/_internal/js_dev_runtime/patch/isolate_patch.dart b/sdk/lib/_internal/js_dev_runtime/patch/isolate_patch.dart index 090b8ccda16..3e858c866bd 100644 --- a/sdk/lib/_internal/js_dev_runtime/patch/isolate_patch.dart +++ b/sdk/lib/_internal/js_dev_runtime/patch/isolate_patch.dart @@ -22,9 +22,15 @@ class Isolate { @patch static Future get packageConfig => _unsupported(); + @patch + static Uri? get packageConfigSync => _unsupported(); + @patch static Future resolvePackageUri(Uri packageUri) => _unsupported(); + @patch + static Uri? resolvePackageUriSync(Uri packageUri) => _unsupported(); + @patch static Future spawn(void entryPoint(T message), T message, {bool paused = false, diff --git a/sdk/lib/_internal/js_runtime/lib/isolate_patch.dart b/sdk/lib/_internal/js_runtime/lib/isolate_patch.dart index d2d6b45da2a..48f4aa4cef4 100644 --- a/sdk/lib/_internal/js_runtime/lib/isolate_patch.dart +++ b/sdk/lib/_internal/js_runtime/lib/isolate_patch.dart @@ -26,11 +26,21 @@ class Isolate { throw new UnsupportedError("Isolate.packageConfig"); } + @patch + static Uri? get packageConfigSync { + throw new UnsupportedError("Isolate.packageConfigSync"); + } + @patch static Future resolvePackageUri(Uri packageUri) { throw new UnsupportedError("Isolate.resolvePackageUri"); } + @patch + static Uri? resolvePackageUriSync(Uri packageUri) { + throw new UnsupportedError("Isolate.resolvePackageUriSync"); + } + @patch static Future spawn(void entryPoint(T message), T message, {bool paused = false, diff --git a/sdk/lib/_internal/vm/bin/builtin.dart b/sdk/lib/_internal/vm/bin/builtin.dart index 2684e6fd4de..82bce74adc6 100644 --- a/sdk/lib/_internal/vm/bin/builtin.dart +++ b/sdk/lib/_internal/vm/bin/builtin.dart @@ -589,11 +589,11 @@ String _resolveScriptUri(String scriptName) { @pragma("vm:entry-point") _setupHooks() { _setupCompleted = true; - VMLibraryHooks.packageConfigUriFuture = _getPackageConfigFuture; - VMLibraryHooks.resolvePackageUriFuture = _resolvePackageUriFuture; + VMLibraryHooks.packageConfigUriSync = _getPackageConfigSync; + VMLibraryHooks.resolvePackageUriSync = _resolvePackageUriSync; } -Future _getPackageConfigFuture() { +Uri? _getPackageConfigSync() { if (_traceLoading) { _log("Request for package config from user code."); } @@ -601,10 +601,10 @@ Future _getPackageConfigFuture() { _requestPackagesMap(_packagesConfigUri); } // Respond with the packages config (if any) after package resolution. - return Future.value(_packageConfig); + return _packageConfig; } -Future _resolvePackageUriFuture(Uri packageUri) { +Uri? _resolvePackageUriSync(Uri packageUri) { if (_traceLoading) { _log("Request for package Uri resolution from user code: $packageUri"); } @@ -613,7 +613,7 @@ Future _resolvePackageUriFuture(Uri packageUri) { _log("Non-package Uri, returning unmodified: $packageUri"); } // Return the incoming parameter if not passed a package: URI. - return Future.value(packageUri); + return packageUri; } if (!_packagesReady) { _requestPackagesMap(_packagesConfigUri); @@ -630,5 +630,5 @@ Future _resolvePackageUriFuture(Uri packageUri) { if (_traceLoading) { _log("Resolved '$packageUri' to '$resolvedUri'"); } - return Future.value(resolvedUri); + return resolvedUri; } diff --git a/sdk/lib/_internal/vm/lib/internal_patch.dart b/sdk/lib/_internal/vm/lib/internal_patch.dart index 7b03d9fda23..8290e5341e2 100644 --- a/sdk/lib/_internal/vm/lib/internal_patch.dart +++ b/sdk/lib/_internal/vm/lib/internal_patch.dart @@ -86,8 +86,8 @@ class VMLibraryHooks { // Implementation of package root/map provision. static String? packageRootString; static String? packageConfigString; - static Future Function()? packageConfigUriFuture; - static Future Function(Uri)? resolvePackageUriFuture; + static Uri? Function()? packageConfigUriSync; + static Uri? Function(Uri)? resolvePackageUriSync; static Uri Function()? _computeScriptUri; static Uri? _cachedScript; diff --git a/sdk/lib/_internal/vm/lib/isolate_patch.dart b/sdk/lib/_internal/vm/lib/isolate_patch.dart index 63c7c4d01c8..8f277a6a25b 100644 --- a/sdk/lib/_internal/vm/lib/isolate_patch.dart +++ b/sdk/lib/_internal/vm/lib/isolate_patch.dart @@ -2,11 +2,6 @@ // 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. -/// Note: the VM concatenates all patch files into a single patch file. This -/// file is the first patch in "dart:isolate" which contains all the imports -/// used by patches of that library. We plan to change this when we have a -/// shared front end and simply use parts. - import "dart:_internal" show ClassID, VMLibraryHooks, patch; import "dart:async" @@ -315,7 +310,12 @@ final class Isolate { @patch static Future get packageConfig { - var hook = VMLibraryHooks.packageConfigUriFuture; + return Future.value(packageConfigSync); + } + + @patch + static Uri? get packageConfigSync { + var hook = VMLibraryHooks.packageConfigUriSync; if (hook == null) { throw new UnsupportedError("Isolate.packageConfig"); } @@ -324,16 +324,21 @@ final class Isolate { @patch static Future resolvePackageUri(Uri packageUri) { - var hook = VMLibraryHooks.resolvePackageUriFuture; + return Future.value(resolvePackageUriSync(packageUri)); + } + + @patch + static Uri? resolvePackageUriSync(Uri packageUri) { + var hook = VMLibraryHooks.resolvePackageUriSync; if (hook == null) { - throw new UnsupportedError("Isolate.resolvePackageUri"); + throw new UnsupportedError("Isolate.resolvePackageUriSync"); } return hook(packageUri); } static bool _packageSupported() => - (VMLibraryHooks.packageConfigUriFuture != null) && - (VMLibraryHooks.resolvePackageUriFuture != null); + (VMLibraryHooks.packageConfigUriSync != null) && + (VMLibraryHooks.resolvePackageUriSync != null); @patch static Future spawn(void entryPoint(T message), T message, @@ -357,7 +362,7 @@ final class Isolate { if (Isolate._packageSupported()) { // resolving script uri is not really necessary, but can be useful // for better failed-to-lookup-function-in-a-script spawn errors. - script = await Isolate.resolvePackageUri(script); + script = Isolate.resolvePackageUriSync(script); } } @@ -426,7 +431,7 @@ final class Isolate { // Inherit this isolate's package resolution setup if not overridden. if (!automaticPackageResolution && packageConfig == null) { if (Isolate._packageSupported()) { - packageConfig = await Isolate.packageConfig; + packageConfig = Isolate.packageConfigSync; } } @@ -435,7 +440,7 @@ final class Isolate { // Avoid calling resolvePackageUri if not strictly necessary in case // the API is not supported. if (packageConfig.isScheme("package")) { - packageConfig = await Isolate.resolvePackageUri(packageConfig); + packageConfig = Isolate.resolvePackageUriSync(packageConfig); } } diff --git a/sdk/lib/_internal/wasm/lib/isolate_patch.dart b/sdk/lib/_internal/wasm/lib/isolate_patch.dart index 9decf7edb51..46082f30433 100644 --- a/sdk/lib/_internal/wasm/lib/isolate_patch.dart +++ b/sdk/lib/_internal/wasm/lib/isolate_patch.dart @@ -25,11 +25,21 @@ class Isolate { throw UnsupportedError("Isolate.packageConfig"); } + @patch + static Uri? get packageConfigSync { + throw UnsupportedError("Isolate.packageConfigSync"); + } + @patch static Future resolvePackageUri(Uri packageUri) { throw UnsupportedError("Isolate.resolvePackageUri"); } + @patch + static Uri? resolvePackageUriSync(Uri packageUri) { + throw UnsupportedError("Isolate.resolvePackageUriSync"); + } + @patch static Future spawn(void entryPoint(T message), T message, {bool paused = false, diff --git a/sdk/lib/isolate/isolate.dart b/sdk/lib/isolate/isolate.dart index 7e46ad7676c..65ee7dadfbb 100644 --- a/sdk/lib/isolate/isolate.dart +++ b/sdk/lib/isolate/isolate.dart @@ -318,20 +318,84 @@ final class Isolate { /// is a sure way to hang your program. external static Isolate get current; - /// The location of the package configuration of the current isolate, if any. + /// The location of the package configuration file of the current isolate. /// - /// If the isolate has not been setup for package resolution, - /// this location is `null`, - /// otherwise it is a URI referencing the package config file. + /// If the isolate was spawned without specifying its package configuration + /// file then the returned value is `null`. + /// + /// Otherwise, the returned value is an absolute URI specifying the location + /// of isolate's package configuration file. + /// + /// The package configuration file is usually named `package_config.json`, + /// and you can use [`package:package_config`](https://pub.dev/documentation/package_config/latest/) + /// to read and parse it. external static Future get packageConfig; - /// Maps a `package:` URI to a non-package Uri. + /// The location of the package configuration file of the current isolate. /// - /// If there is no valid mapping from the `package:` URI in the current - /// isolate, then this call returns `null`. Non-`package:` URIs are - /// returned unmodified. + /// If the isolate was spawned without specifying its package configuration + /// file then the returned value is `null`. + /// + /// Otherwise, the returned value is an absolute URI specifying the location + /// of isolate's package configuration file. + /// + /// The package configuration file is usually named `package_config.json`, + /// and you can use [`package:package_config`](https://pub.dev/documentation/package_config/latest/) + /// to read and parse it. + @Since('3.1') + external static Uri? get packageConfigSync; + + /// Resolves a `package:` URI to its actual location. + /// + /// Returns the actual location of the file or directory specified by the + /// [packageUri] `package:` URI. + /// + /// If the [packageUri] is not a `package:` URI, it's returned as-is. + /// + /// Returns `null` if [packageUri] is a `package:` URI, but either + /// the current package configuration does not have a configuration + /// for the package name of the URI, or + /// the URI is not valid (doesn't start with `package:valid_package_name/`), + /// + /// A `package:` URI is resolved to its actual location based on + /// a package resolution configuration (see [packageConfig]) + /// which specifies how to find the actual location of the file or directory + /// that the `package:` URI points to. + /// + /// The actual location corresponding to a `package:` URI is always a + /// non-`package:` URI, typically a `file:` or possibly `http:` URI. + /// + /// A program may be run in a way where source files are not available, + /// and if so, the returned URI may not correspond to the actual file or + /// directory or be `null`. external static Future resolvePackageUri(Uri packageUri); + /// Resolves a `package:` URI to its actual location. + /// + /// Returns the actual location of the file or directory specified by the + /// [packageUri] `package:` URI. + /// + /// If the [packageUri] is not a `package:` URI, it's returned as-is. + /// + /// Returns `null` if [packageUri] is a `package:` URI, but either + /// the current package configuration does not have a configuration + /// for the package name of the URI, or + /// the URI is not valid (doesn't start with `package:valid_package_name/`), + /// + /// A `package:` URI is resolved to its actual location based on + /// a package resolution configuration (see [packageConfig]) + /// which specifies how to find the actual location of the file or directory + /// that the `package:` URI points to. + /// + /// The actual location corresponding to a `package:` URI is always a + /// non-`package:` URI, typically a `file:` or possibly `http:` URI. + /// + /// A program may be run in a way where source files are not available, + /// and if so, the returned URI may not correspond to the actual file or + /// directory or be `null`. + @Since('3.1') + external static Uri? resolvePackageUriSync(Uri packageUri); + /// Creates and spawns an isolate that shares the same code as the current /// isolate. /// @@ -464,9 +528,7 @@ final class Isolate { /// Returns a future that will complete with an [Isolate] instance if the /// spawning succeeded. It will complete with an error otherwise. external static Future spawnUri( - Uri uri, - List args, - var message, + Uri uri, List args, var message, {bool paused = false, SendPort? onExit, SendPort? onError, @@ -474,11 +536,10 @@ final class Isolate { bool? checked, Map? environment, @Deprecated('The packages/ dir is not supported in Dart 2') - Uri? packageRoot, + Uri? packageRoot, Uri? packageConfig, bool automaticPackageResolution = false, - @Since("2.3") - String? debugName}); + @Since("2.3") String? debugName}); /// Requests the isolate to pause. /// diff --git a/tests/lib/isolate/package_resolve_test.dart b/tests/lib/isolate/package_resolve_test.dart index 22740d97497..ceec284222a 100644 --- a/tests/lib/isolate/package_resolve_test.dart +++ b/tests/lib/isolate/package_resolve_test.dart @@ -5,7 +5,7 @@ import 'dart:io'; import 'dart:isolate'; -final packageUriToResolve = "package:foo/bar.dart"; +final packageUriToResolve = Uri.parse("package:foo/bar.dart"); final packageResolvedUri = "file:///no/such/directory/lib/bar.dart"; final packageConfigJson = """ @@ -54,8 +54,13 @@ testPackageResolution(port) async { try { var packageConfigStr = Platform.packageConfig; var packageConfig = await Isolate.packageConfig; - var resolvedPkg = - await Isolate.resolvePackageUri(Uri.parse(packageUriToResolve)); + if (packageConfig != Isolate.packageConfigSync) { + throw "Isolate.packageConfig != Isolate.packageConfigSync"; + } + var resolvedPkg = await Isolate.resolvePackageUri(packageUriToResolve); + if (resolvedPkg != Isolate.resolvePackageUriSync(packageUriToResolve)) { + throw "Isolate.resolvePackageUri != Isolate.resolvePackageUriSync"; + } print("Spawned isolate's package config flag: $packageConfigStr"); print("Spawned isolate's loaded package config: $packageConfig"); print("Spawned isolate's resolved package path: $resolvedPkg"); diff --git a/tests/lib/isolate/resolve_package_uri_test.dart b/tests/lib/isolate/resolve_package_uri_test.dart deleted file mode 100644 index 00d9ddbc314..00000000000 --- a/tests/lib/isolate/resolve_package_uri_test.dart +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) 2017, 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. - -// Regression test for faulty encoding of `Isolate.resolvePackageUri` by -// dart2js. - -import 'dart:isolate'; - -main() { - var uri = Isolate.resolvePackageUri(Uri.parse('memory:main.dart')); - print(uri); -} diff --git a/tests/lib/isolate/scenarios/automatic_resolution_spec/package_resolve_test.dart b/tests/lib/isolate/scenarios/automatic_resolution_spec/package_resolve_test.dart index 4b2eba651a2..236d92034d1 100644 --- a/tests/lib/isolate/scenarios/automatic_resolution_spec/package_resolve_test.dart +++ b/tests/lib/isolate/scenarios/automatic_resolution_spec/package_resolve_test.dart @@ -5,8 +5,8 @@ import 'dart:io'; import 'dart:isolate'; -final PACKAGE_URI = "package:foo/bar.dart"; -final PACKAGE_PATH = "file:///no/such/directory/bar.dart"; +final packageUriToResolve = Uri.parse("package:foo/bar.dart"); +final packagePath = "file:///no/such/directory/bar.dart"; main([args, port]) async { if (port != null) { @@ -27,7 +27,7 @@ main([args, port]) async { throw "Bad package config in child isolate: ${msg[0]}\n" "Expected: $child_pkg_config"; } - if (msg[1] != PACKAGE_PATH) { + if (msg[1] != packagePath) { throw "Package path not matching: ${msg[1]}"; } print("SUCCESS"); @@ -38,7 +38,13 @@ testPackageResolution(port) async { try { var packageConfigStr = Platform.packageConfig; var packageConfig = await Isolate.packageConfig; - var resolvedPkg = await Isolate.resolvePackageUri(Uri.parse(PACKAGE_URI)); + if (packageConfig != Isolate.packageConfigSync) { + throw "Isolate.packageConfig != Isolate.packageConfigSync"; + } + var resolvedPkg = await Isolate.resolvePackageUri(packageUriToResolve); + if (resolvedPkg != Isolate.resolvePackageUriSync(packageUriToResolve)) { + throw "Isolate.resolvePackageUri != Isolate.resolvePackageUriSync"; + } print("Spawned isolate's package config flag: $packageConfigStr"); print("Spawned isolate's loaded package config: $packageConfig"); print("Spawned isolate's resolved package path: $resolvedPkg"); diff --git a/tests/lib/isolate/scenarios/bad_resolve_package/bad_resolve_package_test.dart b/tests/lib/isolate/scenarios/bad_resolve_package/bad_resolve_package_test.dart index 2c8e42650f4..114b7ae76a9 100644 --- a/tests/lib/isolate/scenarios/bad_resolve_package/bad_resolve_package_test.dart +++ b/tests/lib/isolate/scenarios/bad_resolve_package/bad_resolve_package_test.dart @@ -7,9 +7,11 @@ import 'dart:io'; import 'dart:isolate'; +final packageUriToResolve = Uri.parse("package:asdf/qwerty.dart"); + main([args, port]) async { if (port != null) { - testBadResolvePackage(port); + testPackageResolution(port); return; } var p = new RawReceivePort(); @@ -21,7 +23,7 @@ main([args, port]) async { throw "Failure return from spawned isolate:\n\n$msg"; } // Expecting a null resolution for inexistent package mapping. - if (msg[0] != null) { + if (msg[1] != null) { throw "Bad package config in child isolate: ${msg[0]}\n" "Expected: 'Foo'"; } @@ -29,16 +31,21 @@ main([args, port]) async { }; } -testBadResolvePackage(port) async { +testPackageResolution(port) async { try { var packageConfigStr = Platform.packageConfig; var packageConfig = await Isolate.packageConfig; - var badPackageUri = Uri.parse("package:asdf/qwerty.dart"); - var resolvedPkg = await Isolate.resolvePackageUri(badPackageUri); + if (packageConfig != Isolate.packageConfigSync) { + throw "Isolate.packageConfig != Isolate.packageConfigSync"; + } + var resolvedPkg = await Isolate.resolvePackageUri(packageUriToResolve); + if (resolvedPkg != Isolate.resolvePackageUriSync(packageUriToResolve)) { + throw "Isolate.resolvePackageUri != Isolate.resolvePackageUriSync"; + } print("Spawned isolate's package config flag: $packageConfigStr"); print("Spawned isolate's loaded package config: $packageConfig"); print("Spawned isolate's resolved package path: $resolvedPkg"); - port.send([resolvedPkg?.toString()]); + port.send([packageConfig?.toString(), resolvedPkg?.toString()]); } catch (e, s) { port.send("$e\n$s\n"); } diff --git a/tests/lib/lib.status b/tests/lib/lib.status index 991e72bfc31..16b44a3fe73 100644 --- a/tests/lib/lib.status +++ b/tests/lib/lib.status @@ -115,7 +115,6 @@ isolate/int32_length_overflow_test: SkipSlow [ $compiler != dartk || $runtime != vm ] isolate/package_config_test: SkipByDesign # Uses Isolate.packageConfig isolate/package_resolve_test: SkipByDesign # Uses Isolate.resolvePackageUri -isolate/package_root_test: SkipByDesign # Uses Isolate.packageRoot isolate/scenarios/*: SkipByDesign # Use automatic package resolution, spawnFunction and .dart URIs. isolate/spawn_uri_fail_test: SkipByDesign # Uses dart:io. diff --git a/tests/lib_2/isolate/package_resolve_test.dart b/tests/lib_2/isolate/package_resolve_test.dart index b315b40d214..72cfbe588e3 100644 --- a/tests/lib_2/isolate/package_resolve_test.dart +++ b/tests/lib_2/isolate/package_resolve_test.dart @@ -56,8 +56,14 @@ testPackageResolution(port) async { try { var packageConfigStr = Platform.packageConfig; var packageConfig = await Isolate.packageConfig; + if (packageConfig != Isolate.packageConfigSync) { + throw "Isolate.packageConfig != Isolate.packageConfigSync"; + } var resolvedPkg = await Isolate.resolvePackageUri(Uri.parse(packageUriToResolve)); + if (resolvedPkg != Isolate.resolvePackageUriSync(packageUriToResolve)) { + throw "Isolate.resolvePackageUri != Isolate.resolvePackageUriSync"; + } print("Spawned isolate's package config flag: $packageConfigStr"); print("Spawned isolate's loaded package config: $packageConfig"); print("Spawned isolate's resolved package path: $resolvedPkg"); diff --git a/tests/lib_2/isolate/resolve_package_uri_test.dart b/tests/lib_2/isolate/resolve_package_uri_test.dart deleted file mode 100644 index d47c9d7b608..00000000000 --- a/tests/lib_2/isolate/resolve_package_uri_test.dart +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2017, 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. - -// @dart = 2.9 - -// Regression test for faulty encoding of `Isolate.resolvePackageUri` by -// dart2js. - -import 'dart:isolate'; - -main() { - Future uri = Isolate.resolvePackageUri(Uri.parse('memory:main.dart')); - print(uri); -} diff --git a/tests/lib_2/isolate/scenarios/automatic_resolution_spec/package_resolve_test.dart b/tests/lib_2/isolate/scenarios/automatic_resolution_spec/package_resolve_test.dart index ccd908838ac..7c945911f80 100644 --- a/tests/lib_2/isolate/scenarios/automatic_resolution_spec/package_resolve_test.dart +++ b/tests/lib_2/isolate/scenarios/automatic_resolution_spec/package_resolve_test.dart @@ -7,8 +7,8 @@ import 'dart:io'; import 'dart:isolate'; -final PACKAGE_URI = "package:foo/bar.dart"; -final PACKAGE_PATH = "file:///no/such/directory/bar.dart"; +final packageUriToResolve = Uri.parse("package:foo/bar.dart"); +final packagePath = "file:///no/such/directory/bar.dart"; main([args, port]) async { if (port != null) { @@ -29,7 +29,7 @@ main([args, port]) async { throw "Bad package config in child isolate: ${msg[0]}\n" "Expected: $child_pkg_config"; } - if (msg[1] != PACKAGE_PATH) { + if (msg[1] != packagePath) { throw "Package path not matching: ${msg[1]}"; } print("SUCCESS"); @@ -40,7 +40,13 @@ testPackageResolution(port) async { try { var packageConfigStr = Platform.packageConfig; var packageConfig = await Isolate.packageConfig; - var resolvedPkg = await Isolate.resolvePackageUri(Uri.parse(PACKAGE_URI)); + if (packageConfig != Isolate.packageConfigSync) { + throw "Isolate.packageConfig != Isolate.packageConfigSync"; + } + var resolvedPkg = await Isolate.resolvePackageUri(packageUriToResolve); + if (resolvedPkg != Isolate.resolvePackageUriSync(packageUriToResolve)) { + throw "Isolate.resolvePackageUri != Isolate.resolvePackageUriSync"; + } print("Spawned isolate's package config flag: $packageConfigStr"); print("Spawned isolate's loaded package config: $packageConfig"); print("Spawned isolate's resolved package path: $resolvedPkg"); diff --git a/tests/lib_2/isolate/scenarios/bad_resolve_package/bad_resolve_package_test.dart b/tests/lib_2/isolate/scenarios/bad_resolve_package/bad_resolve_package_test.dart index a2a81f021c3..e5952295151 100644 --- a/tests/lib_2/isolate/scenarios/bad_resolve_package/bad_resolve_package_test.dart +++ b/tests/lib_2/isolate/scenarios/bad_resolve_package/bad_resolve_package_test.dart @@ -36,7 +36,13 @@ testBadResolvePackage(port) async { var packageConfigStr = Platform.packageConfig; var packageConfig = await Isolate.packageConfig; var badPackageUri = Uri.parse("package:asdf/qwerty.dart"); + if (packageConfig != Isolate.packageConfigSync) { + throw "Isolate.packageConfig != Isolate.packageConfigSync"; + } var resolvedPkg = await Isolate.resolvePackageUri(badPackageUri); + if (resolvedPkg != Isolate.resolvePackageUriSync(badPackageUri)) { + throw "Isolate.resolvePackageUri != Isolate.resolvePackageUriSync"; + } print("Spawned isolate's package config flag: $packageConfigStr"); print("Spawned isolate's loaded package config: $packageConfig"); print("Spawned isolate's resolved package path: $resolvedPkg"); diff --git a/tests/lib_2/lib_2.status b/tests/lib_2/lib_2.status index 58197741d8c..6d9e7cfb515 100644 --- a/tests/lib_2/lib_2.status +++ b/tests/lib_2/lib_2.status @@ -93,7 +93,6 @@ isolate/int32_length_overflow_test: SkipSlow [ $compiler != dartk || $runtime != vm ] isolate/package_config_test: SkipByDesign # Uses Isolate.packageConfig isolate/package_resolve_test: SkipByDesign # Uses Isolate.resolvePackageUri -isolate/package_root_test: SkipByDesign # Uses Isolate.packageRoot isolate/scenarios/*: SkipByDesign # Use automatic package resolution, spawnFunction and .dart URIs. isolate/spawn_uri_fail_test: SkipByDesign # Uses dart:io. diff --git a/tests/standalone/packages_file_test.dart b/tests/standalone/packages_file_test.dart deleted file mode 100644 index 57fe3bf20b3..00000000000 --- a/tests/standalone/packages_file_test.dart +++ /dev/null @@ -1,1007 +0,0 @@ -// Copyright (c) 2016, 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:io"; -import "dart:convert" show json; -import "package:path/path.dart" as p; -import "package:async_helper/async_helper.dart"; - -/// Root directory of generated files. -/// Path contains trailing slash. -/// Each configuration gets its own sub-directory. -Directory fileRoot; - -/// Shared HTTP server serving the files in [httpFiles]. -/// Each configuration gets its own "sub-dir" entry in `httpFiles`. -HttpServer httpServer; - -/// Directory structure served by HTTP server. -Map httpFiles = {}; - -/// List of configurations. -List configurations = []; - -/// Collection of failing tests and their failure messages. -/// -/// Each test may fail in more than one way. -var failingTests = >{}; - -main() async { - asyncStart(); - await setUp(); - - await runTests(); // //# 01: ok - await runTests([spawn]); // //# 02: ok - await runTests([spawn, spawn]); // //# 03: ok - await runTests([spawnUriInherit]); // //# 04: ok - await runTests([spawnUriInherit, spawn]); // //# 05: ok - await runTests([spawn, spawnUriInherit]); // //# 06: ok - - // Test that spawning a new VM with file paths instead of URIs as arguments - // gives the same URIs in the internal values. - await runTests([asPath]); // //# 07: ok - - // Test that spawnUri can reproduce the behavior of VM command line parameters - // exactly. - // (Don't run all configuration combinations in the same test, so - // unroll the configurations into multiple groups and run each group - // as its own multitest. - { - var groupCount = 8; - var groups = new List.generate(8, (_) => []); - for (int i = 0; i < configurations.length; i++) { - groups[i % groupCount].add(configurations[i]); - } - var group = -1; - group = 0; // //# 10: ok - group = 1; // //# 11: ok - group = 2; // //# 12: ok - group = 3; // //# 13: ok - group = 4; // //# 14: ok - group = 5; // //# 15: ok - group = 6; // //# 16: ok - group = 7; // //# 17: ok - if (group >= 0) { - for (var other in groups[group]) { - await runTests([spawnUriOther(other)]); - } - } - } - - await tearDown(); - - if (failingTests.isNotEmpty) { - print("Errors found in tests:"); - failingTests.forEach((test, actual) { - print("$test:\n ${actual.join("\n ")}"); - }); - exit(255); - } - - asyncEnd(); -} - -/// Test running the test of the configuration through [Isolate.spawn]. -/// -/// This should not change the expected results compared to running it -/// directly. -Configuration spawn(Configuration conf) { - return conf.update( - description: conf.description + "/spawn", - main: "spawnMain", - newArgs: [conf.mainType], - expect: null); -} - -/// Tests running a spawnUri on top of the configuration before testing. -/// -/// The `spawnUri` call has no explicit root or config parameter, and -/// shouldn't search for one, so it implicitly inherits the current isolate's -/// actual root or configuration. -Configuration spawnUriInherit(Configuration conf) { - if (conf.expect["iroot"] == null && - conf.expect["iconf"] == null && - conf.expect["pconf"] != null) { - // This means that the specified configuration file didn't exist. - // spawning a new URI to "inherit" that will actually do an automatic - // package resolution search with results that are unpredictable. - // That behavior will be tested in a setting where we have more control over - // the files around the spawned URI. - return null; - } - return conf.update( - description: conf.description + "/spawnUri-inherit", - main: "spawnUriMain", - // encode null parameters as "-". Windows fails if using empty string. - newArgs: [ - conf.mainFile, - "-", - "-", - "false" - ], - expect: { - "proot": conf.expect["iroot"], - "pconf": conf.expect["iconf"], - }); -} - -/// Tests running a spawnUri with an explicit configuration different -/// from the original configuration. -/// -/// Duplicates the explicit parameters as arguments to the spawned isolate. -ConfigurationTransformer spawnUriOther(Configuration other) { - return (Configuration conf) { - bool search = (other.config == null) && (other.root == null); - return conf.update( - description: "${conf.description} -spawnUri-> ${other.description}", - main: "spawnUriMain", - newArgs: [ - other.mainFile, - other.config ?? "-", - other.root ?? "-", - "$search" - ], - expect: other.expect); - }; -} - -/// Convert command line parameters to file paths. -/// -/// This only works on the command line, not with `spawnUri`. -Configuration asPath(Configuration conf) { - bool change = false; - - String toPath(String string) { - if (string == null) return null; - if (string.startsWith("file:")) { - change = true; - return new File.fromUri(Uri.parse(string)).path; - } - return string; - } - - var mainFile = toPath(conf.mainFile); - var root = toPath(conf.root); - var config = toPath(conf.config); - if (!change) return null; - return conf.update( - description: conf.description + "/as path", - mainFile: mainFile, - root: root, - config: config); -} - -/// -------------------------------------------------------------- - -Future setUp() async { - fileRoot = createTempDir(); - // print("FILES: $fileRoot"); - httpServer = await startServer(httpFiles); - // print("HTTPS: ${httpServer.address.address}:${httpServer.port}"); - createConfigurations(); -} - -Future tearDown() async { - fileRoot.deleteSync(recursive: true); - await httpServer.close(); -} - -typedef Configuration ConfigurationTransformer(Configuration conf); - -Future runTests([List transformations]) async { - outer: - for (var config in configurations) { - if (transformations != null) { - for (int i = transformations.length - 1; i >= 0; i--) { - config = transformations[i](config); - if (config == null) { - continue outer; // Can be used to skip some tests. - } - } - } - await testConfiguration(config); - } -} - -// Creates a combination of configurations for running the Dart VM. -// -// The combinations covers most configurations of implicit and explicit -// package configurations over both file: and http: file sources. -// It also specifies the expected values of the following for a VM -// run in that configuration. -// -// * `Process.packageRoot` -// * `Process.packageConfig` -// * `Isolate.packageRoot` -// * `Isolate.packageRoot` -// * `Isolate.resolvePackageUri` of various inputs. -// * A variable defined in a library loaded using a `package:` URI. -// -// The configurations all have URIs as `root`, `config` and `mainFile` strings, -// have empty argument lists and `mainFile` points to the `main.dart` file. -void createConfigurations() { - add(String description, String mainDir, - {String root, String config, Map file, Map http, Map expect}) { - var id = freshName("conf"); - - file ??= {}; - http ??= {}; - - // Fix-up paths. - String fileUri = fileRoot.uri.resolve("$id/").toString(); - String httpUri = - "http://${httpServer.address.address}:${httpServer.port}/$id/"; - - String fixPath(String path) { - return path?.replaceAllMapped(fileHttpRegexp, (match) { - if (path.startsWith("%file/", match.start)) return fileUri; - return httpUri; - }); - } - - void fixPaths(Map dirs) { - for (var name in dirs.keys) { - var value = dirs[name]; - if (value is Map) { - Map subDir = value; - fixPaths(subDir); - } else { - var newValue = fixPath(value); - if (newValue != value) dirs[name] = newValue; - } - } - } - - if (!mainDir.endsWith("/")) mainDir += "/"; - // Insert main files into the main-dir map. - Map mainDirMap; - { - if (mainDir.startsWith("%file/")) { - mainDirMap = file; - } else { - mainDirMap = http; - } - var parts = mainDir.split('/'); - for (int i = 1; i < parts.length - 1; i++) { - var dirName = parts[i]; - mainDirMap = mainDirMap[dirName] ?? (mainDirMap[dirName] = {}); - } - } - - mainDirMap["main"] = testMain; - mainDirMap["spawnMain"] = spawnMain.replaceAll("%mainDir/", mainDir); - mainDirMap["spawnUriMain"] = spawnUriMain; - - mainDir = fixPath(mainDir); - root = fixPath(root); - config = fixPath(config); - fixPaths(file); - fixPaths(http); - // These expectations are default. If not overridden the value will be - // expected to be null. That is, you can't avoid testing the actual - // value of these, you can only change what value to expect. - // For values not included here (commented out), the result is not tested - // unless a value (maybe null) is provided. - fixPaths(expect); - - expect = { - "pconf": null, - "proot": null, - "iconf": null, - "iroot": null, - "foo": null, - "foo/": null, - "foo/bar": null, - "foo.x": "qux", - "bar/bar": null, - "relative": "relative/path", - "nonpkg": "http://example.org/file" - }..addAll(expect ?? const {}); - - // Add http files to the http server. - if (http.isNotEmpty) { - httpFiles[id] = http; - } - // Add file files to the file system. - if (file.isNotEmpty) { - createFiles(fileRoot, id, file); - } - - configurations.add(new Configuration( - description: description, - root: root, - config: config, - mainFile: mainDir + "main.dart", - args: const [], - expect: expect)); - } - - // The `test` function can generate file or http resources. - // It replaces "%file/" with URI of the root directory of generated files and - // "%http/" with the URI of the HTTP server's root in appropriate contexts - // (all file contents and parameters). - - // Tests that only use one scheme to access files. - for (var scheme in ["file", "http"]) { - /// Run a test in the current scheme. - /// - /// The files are served either through HTTP or in a local directory. - /// Use "%$scheme/" to refer to the root of the served files. - addScheme(description, main, {expect, files, args, root, config}) { - add("$scheme/$description", main, - expect: expect, - root: root, - config: config, - file: (scheme == "file") ? files : null, - http: (scheme == "http") ? files : null); - } - - { - // No parameters, no .packages files or packages/ dir. - // A "file:" source realizes there is no configuration and can't resolve - // any packages, but a "http:" source assumes a "packages/" directory. - addScheme("no resolution", "%$scheme/", - files: {}, - expect: (scheme == "file") - ? {"foo.x": null} - : { - "iroot": "%http/packages/", - "foo": "%http/packages/foo", - "foo/": "%http/packages/foo/", - "foo/bar": "%http/packages/foo/bar", - "foo.x": null, - "bar/bar": "%http/packages/bar/bar", - }); - } - - { - // No parameters, no .packages files, - // packages/ dir exists and is detected. - var files = {"packages": fooPackage}; - addScheme("implicit packages dir", "%$scheme/", files: files, expect: { - "iroot": "%$scheme/packages/", - "foo": "%$scheme/packages/foo", - "foo/": "%$scheme/packages/foo/", - "foo/bar": "%$scheme/packages/foo/bar", - "bar/bar": "%$scheme/packages/bar/bar", - }); - } - - { - // No parameters, no .packages files in current dir, but one in parent, - // packages/ dir exists and is used. - // - // Should not detect the .packages file in parent directory. - // That file is empty, so if it is used, the system cannot resolve "foo". - var files = { - "sub": {"packages": fooPackage}, - ".packages": "" - }; - addScheme( - "implicit packages dir overrides parent .packages", "%$scheme/sub/", - files: files, - expect: { - "iroot": "%$scheme/sub/packages/", - "foo": "%$scheme/sub/packages/foo", - "foo/": "%$scheme/sub/packages/foo/", - "foo/bar": "%$scheme/sub/packages/foo/bar", - // "foo.x": "qux", // Blocked by issue http://dartbug.com/26482 - "bar/bar": "%$scheme/sub/packages/bar/bar", - }); - } - - { - // No parameters, a .packages file next to entry is found and used. - // A packages/ directory is ignored. - var files = { - ".packages": "foo:pkgs/foo/", - "packages": {}, - "pkgs": fooPackage - }; - addScheme("implicit .packages file", "%$scheme/", files: files, expect: { - "iconf": "%$scheme/.packages", - "foo/": "%$scheme/pkgs/foo/", - "foo/bar": "%$scheme/pkgs/foo/bar", - }); - } - - { - // No parameters, a .packages file in parent dir, no packages/ dir. - // With a file: URI, find the .packages file. - // WIth a http: URI, assume a packages/ dir. - var files = {"sub": {}, ".packages": "foo:pkgs/foo/", "pkgs": fooPackage}; - addScheme(".packages file in parent", "%$scheme/sub/", - files: files, - expect: (scheme == "file") - ? { - "iconf": "%file/.packages", - "foo/": "%file/pkgs/foo/", - "foo/bar": "%file/pkgs/foo/bar", - } - : { - "iroot": "%http/sub/packages/", - "foo": "%http/sub/packages/foo", - "foo/": "%http/sub/packages/foo/", - "foo/bar": "%http/sub/packages/foo/bar", - "foo.x": null, - "bar/bar": "%http/sub/packages/bar/bar", - }); - } - - { - // Specified package root that doesn't exist. - // Ignores existing .packages file and packages/ dir. - addScheme("explicit root not there", "%$scheme/", - files: { - "packages": fooPackage, - ".packages": "foo:%$scheme/packages/" - }, - root: "%$scheme/notthere/", - expect: { - "proot": "%$scheme/notthere/", - "iroot": "%$scheme/notthere/", - "foo": "%$scheme/notthere/foo", - "foo/": "%$scheme/notthere/foo/", - "foo/bar": "%$scheme/notthere/foo/bar", - "foo.x": null, - "bar/bar": "%$scheme/notthere/bar/bar", - }); - } - - { - // Specified package config that doesn't exist. - // Ignores existing .packages file and packages/ dir. - addScheme("explicit config not there", "%$scheme/", - files: {".packages": "foo:packages/foo/", "packages": fooPackage}, - config: "%$scheme/.notthere", - expect: { - "pconf": "%$scheme/.notthere", - "iconf": null, // <- Only there if actually loaded (unspecified). - "foo/": null, - "foo/bar": null, - "foo.x": null, - }); - } - - { - // Specified package root with no trailing slash. - // The Platform.packageRoot and Isolate.packageRoot has a trailing slash. - var files = { - ".packages": "foo:packages/foo/", - "packages": {}, - "pkgs": fooPackage - }; - addScheme("explicit package root, no slash", "%$scheme/", - files: files, - root: "%$scheme/pkgs", - expect: { - "proot": "%$scheme/pkgs/", - "iroot": "%$scheme/pkgs/", - "foo": "%$scheme/pkgs/foo", - "foo/": "%$scheme/pkgs/foo/", - "foo/bar": "%$scheme/pkgs/foo/bar", - "bar/bar": "%$scheme/pkgs/bar/bar", - }); - } - - { - // Specified package root with trailing slash. - var files = { - ".packages": "foo:packages/foo/", - "packages": {}, - "pkgs": fooPackage - }; - addScheme("explicit package root, slash", "%$scheme/", - files: files, - root: "%$scheme/pkgs", - expect: { - "proot": "%$scheme/pkgs/", - "iroot": "%$scheme/pkgs/", - "foo": "%$scheme/pkgs/foo", - "foo/": "%$scheme/pkgs/foo/", - "foo/bar": "%$scheme/pkgs/foo/bar", - "bar/bar": "%$scheme/pkgs/bar/bar", - }); - } - - { - // Specified package config. - var files = { - ".packages": "foo:packages/foo/", - "packages": {}, - ".pkgs": "foo:pkgs/foo/", - "pkgs": fooPackage - }; - addScheme("explicit package config file", "%$scheme/", - files: files, - config: "%$scheme/.pkgs", - expect: { - "pconf": "%$scheme/.pkgs", - "iconf": "%$scheme/.pkgs", - "foo/": "%$scheme/pkgs/foo/", - "foo/bar": "%$scheme/pkgs/foo/bar", - }); - } - - { - // Specified package config as data: URI. - // The package config can be specified as a data: URI. - // (In that case, relative URI references in the config file won't work). - var files = { - ".packages": "foo:packages/foo/", - "packages": {}, - "pkgs": fooPackage - }; - var dataUri = "data:,foo:%$scheme/pkgs/foo/\n"; - addScheme("explicit data: config file", "%$scheme/", - files: files, - config: dataUri, - expect: { - "pconf": dataUri, - "iconf": dataUri, - "foo/": "%$scheme/pkgs/foo/", - "foo/bar": "%$scheme/pkgs/foo/bar", - }); - } - } - - // Tests where there are files on both http: and file: sources. - - for (var entryScheme in const ["file", "http"]) { - for (var pkgScheme in const ["file", "http"]) { - // Package root. - if (entryScheme != pkgScheme) { - // Package dir and entry point on different schemes. - var files = {}; - var https = {}; - (entryScheme == "file" ? files : https)["main"] = testMain; - (pkgScheme == "file" ? files : https)["pkgs"] = fooPackage; - add("$pkgScheme pkg/$entryScheme main", "%$entryScheme/", - file: files, - http: https, - root: "%$pkgScheme/pkgs/", - expect: { - "proot": "%$pkgScheme/pkgs/", - "iroot": "%$pkgScheme/pkgs/", - "foo": "%$pkgScheme/pkgs/foo", - "foo/": "%$pkgScheme/pkgs/foo/", - "foo/bar": "%$pkgScheme/pkgs/foo/bar", - "bar/bar": "%$pkgScheme/pkgs/bar/bar", - "foo.x": "qux", - }); - } - // Package config. The configuration file may also be on either source. - for (var configScheme in const ["file", "http"]) { - // Don't do the boring stuff! - if (entryScheme == configScheme && entryScheme == pkgScheme) continue; - // Package config, packages and entry point not all on same scheme. - var files = {}; - var https = {}; - (entryScheme == "file" ? files : https)["main"] = testMain; - (configScheme == "file" ? files : https)[".pkgs"] = - "foo:%$pkgScheme/pkgs/foo/\n"; - (pkgScheme == "file" ? files : https)["pkgs"] = fooPackage; - add("$pkgScheme pkg/$configScheme config/$entryScheme main", - "%$entryScheme/", - file: files, - http: https, - config: "%$configScheme/.pkgs", - expect: { - "pconf": "%$configScheme/.pkgs", - "iconf": "%$configScheme/.pkgs", - "foo/": "%$pkgScheme/pkgs/foo/", - "foo/bar": "%$pkgScheme/pkgs/foo/bar", - "foo.x": "qux", - }); - } - } - } -} - -// --------------------------------------------------------- -// Helper functionality. - -var fileHttpRegexp = new RegExp(r"%(?:file|http)/"); - -// Executes a test in a configuration. -// -// The test must specify which main file to use -// (`main`, `spawnMain` or `spawnUriMain`) -// and any arguments which will be used by `spawnMain` and `spawnUriMain`. -// -// The [expect] map may be used to override the expectations of the -// configuration on a value-by-value basis. Passing, e.g., `{"pconf": null}` -// will override only the `pconf` (`Platform.packageConfig`) expectation. -Future testConfiguration(Configuration conf) async { - print("-- ${conf.description}"); - var description = conf.description; - try { - var output = await execDart(conf.mainFile, - root: conf.root, config: conf.config, scriptArgs: conf.args); - match(json.decode(output), conf.expect, description, output); - } catch (e, s) { - // Unexpected error calling execDart or parsing the result. - // Report it and continue. - print("ERROR running $description: $e\n$s"); - failingTests.putIfAbsent(description, () => []).add("$e"); - } -} - -/// Test that the output of running testMain matches the expectations. -/// -/// The output is a string which is parse as a JSON literal. -/// The resulting map is always mapping strings to strings, or possibly `null`. -/// The expectations can have non-string values other than null, -/// they are `toString`'ed before being compared (so the caller can use a URI -/// or a File/Directory directly as an expectation). -void match(Map actuals, Map expectations, String desc, String actualJson) { - for (var key in expectations.keys) { - var expectation = expectations[key]?.toString(); - var actual = actuals[key]; - if (expectation != actual) { - print("ERROR: $desc: $key: Expected: <$expectation> Found: <$actual>"); - failingTests - .putIfAbsent(desc, () => []) - .add("$key: $expectation != $actual"); - } - } -} - -const String improt = "import"; // Avoid multitest import rewriting. - -/// Script that prints the current state and the result of resolving -/// a few package URIs. This script will be invoked in different settings, -/// and the result will be parsed and compared to the expectations. -const String testMain = """ -$improt "dart:convert" show json; -$improt "dart:io" show Platform, Directory; -$improt "dart:isolate" show Isolate; -$improt "package:foo/foo.dart" deferred as foo; -main(_) async { - String platformRoot = await Platform.packageRoot; - String platformConfig = await Platform.packageConfig; - Directory cwd = Directory.current; - Uri script = Platform.script; - Uri isolateRoot = await Isolate.packageRoot; - Uri isolateConfig = await Isolate.packageConfig; - Uri base = Uri.base; - Uri res1 = await Isolate.resolvePackageUri(Uri.parse("package:foo")); - Uri res2 = await Isolate.resolvePackageUri(Uri.parse("package:foo/")); - Uri res3 = await Isolate.resolvePackageUri(Uri.parse("package:foo/bar")); - Uri res4 = await Isolate.resolvePackageUri(Uri.parse("package:bar/bar")); - Uri res5 = await Isolate.resolvePackageUri(Uri.parse("relative/path")); - Uri res6 = await Isolate.resolvePackageUri( - Uri.parse("http://example.org/file")); - String fooX = await foo - .loadLibrary() - .timeout(const Duration(seconds: 1)) - .then((_) => foo.x, onError: (_) => null); - print(json.encode({ - "cwd": cwd.path, - "base": base?.toString(), - "script": script?.toString(), - "proot": platformRoot, - "pconf": platformConfig, - "iroot" : isolateRoot?.toString(), - "iconf" : isolateConfig?.toString(), - "foo": res1?.toString(), - "foo/": res2?.toString(), - "foo/bar": res3?.toString(), - "foo.x": fooX?.toString(), - "bar/bar": res4?.toString(), - "relative": res5?.toString(), - "nonpkg": res6?.toString(), - })); -} -"""; - -/// Script that spawns a new Isolate using Isolate.spawnUri. -/// -/// Takes URI of target isolate, package config, package root and -/// automatic package resolution-flag parameters as command line arguments. -/// Any further arguments are forwarded to the spawned isolate. -const String spawnUriMain = """ -$improt "dart:isolate"; -$improt "dart:async"; -main(args) async { - Uri target = Uri.parse(args[0]); - Uri config = (args[1] == "-") ? null : Uri.parse(args[1]); - Uri root = (args[2] == "-") ? null : Uri.parse(args[2]); - bool search = args[3] == "true"; - var restArgs = args.skip(4).toList(); - // Port keeps isolate alive until spawned isolate terminates. - var port = new RawReceivePort(); - port.handler = (res) async { - port.close(); // Close on exit or first error. - if (res != null) { - await new Future.error(res[0], new StackTrace.fromString(res[1])); - } - }; - Isolate.spawnUri(target, restArgs, null, - packageRoot: root, packageConfig: config, - automaticPackageResolution: search, - onError: port.sendPort, onExit: port.sendPort); -} -"""; - -/// Script that spawns a new Isolate using Isolate.spawn. -/// -/// Uses the first argument to select which target to spawn. -/// Should be either "test", "uri" or "spawn". -const String spawnMain = """ -$improt "dart:async"; -$improt "dart:isolate"; -$improt "%mainDir/main.dart" as test; -$improt "%mainDir/spawnUriMain.dart" as spawnUri; -main(List args) async { - // Port keeps isolate alive until spawned isolate terminates. - var port = new RawReceivePort(); - port.handler = (res) async { - port.close(); // Close on exit or first error. - if (res != null) { - await new Future.error(res[0], new StackTrace.fromString(res[1])); - } - }; - var arg = args.first; - var rest = args.skip(1).toList(); - var target; - if (arg == "main") { - target = test.main; - } else if (arg == "spawnUriMain") { - target = spawnUri.main; - } else { - target = main; - } - Isolate.spawn(target, rest, onError: port.sendPort, onExit: port.sendPort); -} -"""; - -/// A package directory containing only one package, "foo", with one file. -const Map fooPackage = const { - "foo": const {"foo": "var x = 'qux';"} -}; - -/// Runs the Dart executable with the provided parameters. -/// -/// Captures and returns the output. -Future execDart(String script, - {String root, String config, Iterable scriptArgs}) async { - var checked = false; - assert((checked = true)); - // TODO: Find a way to change CWD before running script. - var executable = Platform.executable; - var args = []; - if (checked) args.add("--checked"); - if (root != null) args.add("--package-root=$root"); - if (config != null) args.add("--packages=$config"); - args.add(script); - if (scriptArgs != null) { - args.addAll(scriptArgs); - } - return Process.run(executable, args).then((results) { - if (results.exitCode != 0 || results.stderr.isNotEmpty) { - throw results.stderr; - } - return results.stdout; - }); -} - -/// Creates a number of files and subdirectories. -/// -/// The [content] is the content of the directory itself. The map keys are -/// names and the values are either strings that represent Dart file contents -/// or maps that represent subdirectories. -void createFiles(Directory tempDir, String subDir, Map content) { - Directory createDir(Directory base, String name) { - Directory newDir = new Directory(p.join(base.path, name)); - newDir.createSync(); - return newDir; - } - - void createTextFile(Directory base, String name, String content) { - File newFile = new File(p.join(base.path, name)); - newFile.writeAsStringSync(content); - } - - void createRecursive(Directory dir, Map map) { - for (var name in map.keys) { - var content = map[name]; - if (content is String) { - // If the name starts with "." it's a .packages file, otherwise it's - // a dart file. Those are the only files we care about in this test. - createTextFile( - dir, name.startsWith(".") ? name : name + ".dart", content); - } else { - assert(content is Map); - var subdir = createDir(dir, name); - createRecursive(subdir, content); - } - } - } - - createRecursive(createDir(tempDir, subDir), content); -} - -/// Start an HTTP server which serves a directory/file structure. -/// -/// The directories and files are described by [files]. -/// -/// Each map key is an entry in a directory. A `Map` value is a sub-directory -/// and a `String` value is a text file. -/// The file contents are run through [fixPaths] to allow them to be self- -/// referential. -Future startServer(Map files) async { - return (await HttpServer.bind(InternetAddress.loopbackIPv4, 0)) - ..forEach((request) { - var result = files; - onFailure: - { - for (var part in request.uri.pathSegments) { - if (part.endsWith(".dart")) { - part = part.substring(0, part.length - 5); - } - if (result is Map) { - result = result[part]; - } else { - break onFailure; - } - } - if (result is String) { - request.response - ..write(result) - ..close(); - return; - } - } - request.response - ..statusCode = HttpStatus.notFound - ..close(); - }); -} - -// Counter used to avoid reusing temporary file or directory names. -// -// Used when adding extra files to an existing directory structure, -// and when creating temporary directories. -// -// Some platform temporary-directory implementations are timer based, -// and creating two temp-dirs withing a short duration may cause a collision. -int tmpNameCounter = 0; - -// Fresh file name. -String freshName([String base = "tmp"]) => "$base${tmpNameCounter++}"; - -Directory createTempDir() { - return Directory.systemTemp.createTempSync(freshName("pftest-")); -} - -typedef void ConfigUpdate(Configuration configuration); - -/// The configuration for a single test. -class Configuration { - /// The "description" of the test - a description of the set-up. - final String description; - - /// The package root parameter passed to the Dart isolate. - /// - /// At most one of [root] and [config] should be supplied. If both are - /// omitted, a VM will search for a packages file or dir. - final String root; - - /// The package configuration file location passed to the Dart isolate. - final String config; - - /// Path to the main file to run. - final String mainFile; - - /// List of arguments to pass to the main function. - final List args; - - /// The expected values for `Platform.package{Root,Config}`, - /// `Isolate.package{Root,Config}` and resolution of package URIs - /// in a `foo` package. - /// - /// The results are found by running the `main.dart` file inside [mainDir]. - /// The tests can run this file after doing other `spawn` or `spawnUri` calls. - final Map expect; - - Configuration( - {this.description, - this.root, - this.config, - this.mainFile, - this.args, - this.expect}); - - // Gets the type of main file, one of `main`, `spawnMain` or `spawnUriMain`. - String get mainType { - var lastSlash = mainFile.lastIndexOf("/"); - if (lastSlash < 0) { - // Assume it's a Windows path. - lastSlash = mainFile.lastIndexOf(r"\"); - } - var name = mainFile.substring(lastSlash + 1, mainFile.length - 5); - assert(name == "main" || name == "spawnMain" || name == "spawnUriMain"); - return name; - } - - String get mainPath { - var lastSlash = mainFile.lastIndexOf("/"); - if (lastSlash < 0) { - // Assume it's a Windows path. - lastSlash = mainFile.lastIndexOf(r"\"); - } - return mainFile.substring(0, lastSlash + 1); - } - - /// Create a new configuration from the old one. - /// - /// [description] is new description. - /// - /// [main] is one of `main`, `spawnMain` or `spawnUriMain`, and changes - /// the [Configuration.mainFile] to a different file in the same directory. - /// - /// [mainFile] overrides [Configuration.mainFile] completely, and ignores - /// [main]. - /// - /// [newArgs] are prepended to the existing [Configuration.args]. - /// - /// [args] overrides [Configuration.args] completely and ignores [newArgs]. - /// - /// [expect] overrides individual expectations. - /// - /// [root] and [config] overrides the existing values. - Configuration update( - {String description, - String main, - String mainFile, - String root, - String config, - List args, - List newArgs, - Map expect}) { - return new Configuration( - description: description ?? this.description, - root: root ?? this.root, - config: config ?? this.config, - mainFile: mainFile ?? - ((main == null) ? this.mainFile : "${this.mainPath}$main.dart"), - args: args ?? - ([] - ..addAll(newArgs ?? const []) - ..addAll(this.args)), - expect: expect == null ? this.expect : new Map.from(this.expect) - ..addAll(expect ?? const {})); - } - - // For debugging. - String toString() { - return "Configuration($description\n" - " root : $root\n" - " config: $config\n" - " main : $mainFile\n" - " args : ${args.map((x) => '"$x"').join(" ")}\n" - ") : expect {\n${expect.keys.map((k) => ' "$k"'.padRight(6) + ":${json.encode(expect[k])}\n").join()}" - "}"; - } -} - -// Inserts the file with generalized [name] at [path] with [content]. -// -// The [path] is a directory where the file is created. It must start with -// either '%file/' or '%http/' to select the structure to put it into. -// -// The [name] should not have a trailing ".dart" for Dart files. Any file -// not starting with "." is assumed to be a ".dart" file. -void insertFileAt( - Map file, Map http, String path, String name, String content) { - var parts = path.split('/').toList(); - var dir = (parts[0] == "%file") ? file : http; - for (var i = 1; i < parts.length - 1; i++) { - var entry = parts[i]; - dir = dir[entry] ?? (dir[entry] = {}); - } - dir[name] = content; -} diff --git a/tests/standalone/standalone.status b/tests/standalone/standalone.status index 7e0de639f21..7e42c549f1c 100644 --- a/tests/standalone/standalone.status +++ b/tests/standalone/standalone.status @@ -11,8 +11,6 @@ io/large_file_read_small_file_test: Slow, Pass # Test reads small file 1M times io/non_utf8_directory_test: Skip # Issue 33519. Temp files causing bots to go purple. io/non_utf8_file_test: Skip # Issue 33519. Temp files causing bots to go purple. io/non_utf8_link_test: Skip # Issue 33519. Temp files causing bots to go purple. -packages_file_test: Skip # Issue 26715 -packages_file_test/none: Skip # contains no tests. [ $builder_tag == dwarf ] io/socket_connect_stacktrace_test: SkipByDesign # Assumes stacktrace can be inspected directly, without decoding diff --git a/tests/standalone_2/packages_file_test.dart b/tests/standalone_2/packages_file_test.dart deleted file mode 100644 index 26f93461a99..00000000000 --- a/tests/standalone_2/packages_file_test.dart +++ /dev/null @@ -1,1009 +0,0 @@ -// Copyright (c) 2016, 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. - -// @dart = 2.9 - -import "dart:async"; -import "dart:io"; -import "dart:convert" show json; -import "package:path/path.dart" as p; -import "package:async_helper/async_helper.dart"; - -/// Root directory of generated files. -/// Path contains trailing slash. -/// Each configuration gets its own sub-directory. -Directory fileRoot; - -/// Shared HTTP server serving the files in [httpFiles]. -/// Each configuration gets its own "sub-dir" entry in `httpFiles`. -HttpServer httpServer; - -/// Directory structure served by HTTP server. -Map httpFiles = {}; - -/// List of configurations. -List configurations = []; - -/// Collection of failing tests and their failure messages. -/// -/// Each test may fail in more than one way. -var failingTests = >{}; - -main() async { - asyncStart(); - await setUp(); - - await runTests(); // //# 01: ok - await runTests([spawn]); // //# 02: ok - await runTests([spawn, spawn]); // //# 03: ok - await runTests([spawnUriInherit]); // //# 04: ok - await runTests([spawnUriInherit, spawn]); // //# 05: ok - await runTests([spawn, spawnUriInherit]); // //# 06: ok - - // Test that spawning a new VM with file paths instead of URIs as arguments - // gives the same URIs in the internal values. - await runTests([asPath]); // //# 07: ok - - // Test that spawnUri can reproduce the behavior of VM command line parameters - // exactly. - // (Don't run all configuration combinations in the same test, so - // unroll the configurations into multiple groups and run each group - // as its own multitest. - { - var groupCount = 8; - var groups = new List.generate(8, (_) => []); - for (int i = 0; i < configurations.length; i++) { - groups[i % groupCount].add(configurations[i]); - } - var group = -1; - group = 0; // //# 10: ok - group = 1; // //# 11: ok - group = 2; // //# 12: ok - group = 3; // //# 13: ok - group = 4; // //# 14: ok - group = 5; // //# 15: ok - group = 6; // //# 16: ok - group = 7; // //# 17: ok - if (group >= 0) { - for (var other in groups[group]) { - await runTests([spawnUriOther(other)]); - } - } - } - - await tearDown(); - - if (failingTests.isNotEmpty) { - print("Errors found in tests:"); - failingTests.forEach((test, actual) { - print("$test:\n ${actual.join("\n ")}"); - }); - exit(255); - } - - asyncEnd(); -} - -/// Test running the test of the configuration through [Isolate.spawn]. -/// -/// This should not change the expected results compared to running it -/// directly. -Configuration spawn(Configuration conf) { - return conf.update( - description: conf.description + "/spawn", - main: "spawnMain", - newArgs: [conf.mainType], - expect: null); -} - -/// Tests running a spawnUri on top of the configuration before testing. -/// -/// The `spawnUri` call has no explicit root or config parameter, and -/// shouldn't search for one, so it implicitly inherits the current isolate's -/// actual root or configuration. -Configuration spawnUriInherit(Configuration conf) { - if (conf.expect["iroot"] == null && - conf.expect["iconf"] == null && - conf.expect["pconf"] != null) { - // This means that the specified configuration file didn't exist. - // spawning a new URI to "inherit" that will actually do an automatic - // package resolution search with results that are unpredictable. - // That behavior will be tested in a setting where we have more control over - // the files around the spawned URI. - return null; - } - return conf.update( - description: conf.description + "/spawnUri-inherit", - main: "spawnUriMain", - // encode null parameters as "-". Windows fails if using empty string. - newArgs: [ - conf.mainFile, - "-", - "-", - "false" - ], - expect: { - "proot": conf.expect["iroot"], - "pconf": conf.expect["iconf"], - }); -} - -/// Tests running a spawnUri with an explicit configuration different -/// from the original configuration. -/// -/// Duplicates the explicit parameters as arguments to the spawned isolate. -ConfigurationTransformer spawnUriOther(Configuration other) { - return (Configuration conf) { - bool search = (other.config == null) && (other.root == null); - return conf.update( - description: "${conf.description} -spawnUri-> ${other.description}", - main: "spawnUriMain", - newArgs: [ - other.mainFile, - other.config ?? "-", - other.root ?? "-", - "$search" - ], - expect: other.expect); - }; -} - -/// Convert command line parameters to file paths. -/// -/// This only works on the command line, not with `spawnUri`. -Configuration asPath(Configuration conf) { - bool change = false; - - String toPath(String string) { - if (string == null) return null; - if (string.startsWith("file:")) { - change = true; - return new File.fromUri(Uri.parse(string)).path; - } - return string; - } - - var mainFile = toPath(conf.mainFile); - var root = toPath(conf.root); - var config = toPath(conf.config); - if (!change) return null; - return conf.update( - description: conf.description + "/as path", - mainFile: mainFile, - root: root, - config: config); -} - -/// -------------------------------------------------------------- - -Future setUp() async { - fileRoot = createTempDir(); - // print("FILES: $fileRoot"); - httpServer = await startServer(httpFiles); - // print("HTTPS: ${httpServer.address.address}:${httpServer.port}"); - createConfigurations(); -} - -Future tearDown() async { - fileRoot.deleteSync(recursive: true); - await httpServer.close(); -} - -typedef Configuration ConfigurationTransformer(Configuration conf); - -Future runTests([List transformations]) async { - outer: - for (var config in configurations) { - if (transformations != null) { - for (int i = transformations.length - 1; i >= 0; i--) { - config = transformations[i](config); - if (config == null) { - continue outer; // Can be used to skip some tests. - } - } - } - await testConfiguration(config); - } -} - -// Creates a combination of configurations for running the Dart VM. -// -// The combinations covers most configurations of implicit and explicit -// package configurations over both file: and http: file sources. -// It also specifies the expected values of the following for a VM -// run in that configuration. -// -// * `Process.packageRoot` -// * `Process.packageConfig` -// * `Isolate.packageRoot` -// * `Isolate.packageRoot` -// * `Isolate.resolvePackageUri` of various inputs. -// * A variable defined in a library loaded using a `package:` URI. -// -// The configurations all have URIs as `root`, `config` and `mainFile` strings, -// have empty argument lists and `mainFile` points to the `main.dart` file. -void createConfigurations() { - add(String description, String mainDir, - {String root, String config, Map file, Map http, Map expect}) { - var id = freshName("conf"); - - file ??= {}; - http ??= {}; - - // Fix-up paths. - String fileUri = fileRoot.uri.resolve("$id/").toString(); - String httpUri = - "http://${httpServer.address.address}:${httpServer.port}/$id/"; - - String fixPath(String path) { - return path?.replaceAllMapped(fileHttpRegexp, (match) { - if (path.startsWith("%file/", match.start)) return fileUri; - return httpUri; - }); - } - - void fixPaths(Map dirs) { - for (var name in dirs.keys) { - var value = dirs[name]; - if (value is Map) { - Map subDir = value; - fixPaths(subDir); - } else { - var newValue = fixPath(value); - if (newValue != value) dirs[name] = newValue; - } - } - } - - if (!mainDir.endsWith("/")) mainDir += "/"; - // Insert main files into the main-dir map. - Map mainDirMap; - { - if (mainDir.startsWith("%file/")) { - mainDirMap = file; - } else { - mainDirMap = http; - } - var parts = mainDir.split('/'); - for (int i = 1; i < parts.length - 1; i++) { - var dirName = parts[i]; - mainDirMap = mainDirMap[dirName] ?? (mainDirMap[dirName] = {}); - } - } - - mainDirMap["main"] = testMain; - mainDirMap["spawnMain"] = spawnMain.replaceAll("%mainDir/", mainDir); - mainDirMap["spawnUriMain"] = spawnUriMain; - - mainDir = fixPath(mainDir); - root = fixPath(root); - config = fixPath(config); - fixPaths(file); - fixPaths(http); - // These expectations are default. If not overridden the value will be - // expected to be null. That is, you can't avoid testing the actual - // value of these, you can only change what value to expect. - // For values not included here (commented out), the result is not tested - // unless a value (maybe null) is provided. - fixPaths(expect); - - expect = { - "pconf": null, - "proot": null, - "iconf": null, - "iroot": null, - "foo": null, - "foo/": null, - "foo/bar": null, - "foo.x": "qux", - "bar/bar": null, - "relative": "relative/path", - "nonpkg": "http://example.org/file" - }..addAll(expect ?? const {}); - - // Add http files to the http server. - if (http.isNotEmpty) { - httpFiles[id] = http; - } - // Add file files to the file system. - if (file.isNotEmpty) { - createFiles(fileRoot, id, file); - } - - configurations.add(new Configuration( - description: description, - root: root, - config: config, - mainFile: mainDir + "main.dart", - args: const [], - expect: expect)); - } - - // The `test` function can generate file or http resources. - // It replaces "%file/" with URI of the root directory of generated files and - // "%http/" with the URI of the HTTP server's root in appropriate contexts - // (all file contents and parameters). - - // Tests that only use one scheme to access files. - for (var scheme in ["file", "http"]) { - /// Run a test in the current scheme. - /// - /// The files are served either through HTTP or in a local directory. - /// Use "%$scheme/" to refer to the root of the served files. - addScheme(description, main, {expect, files, args, root, config}) { - add("$scheme/$description", main, - expect: expect, - root: root, - config: config, - file: (scheme == "file") ? files : null, - http: (scheme == "http") ? files : null); - } - - { - // No parameters, no .packages files or packages/ dir. - // A "file:" source realizes there is no configuration and can't resolve - // any packages, but a "http:" source assumes a "packages/" directory. - addScheme("no resolution", "%$scheme/", - files: {}, - expect: (scheme == "file") - ? {"foo.x": null} - : { - "iroot": "%http/packages/", - "foo": "%http/packages/foo", - "foo/": "%http/packages/foo/", - "foo/bar": "%http/packages/foo/bar", - "foo.x": null, - "bar/bar": "%http/packages/bar/bar", - }); - } - - { - // No parameters, no .packages files, - // packages/ dir exists and is detected. - var files = {"packages": fooPackage}; - addScheme("implicit packages dir", "%$scheme/", files: files, expect: { - "iroot": "%$scheme/packages/", - "foo": "%$scheme/packages/foo", - "foo/": "%$scheme/packages/foo/", - "foo/bar": "%$scheme/packages/foo/bar", - "bar/bar": "%$scheme/packages/bar/bar", - }); - } - - { - // No parameters, no .packages files in current dir, but one in parent, - // packages/ dir exists and is used. - // - // Should not detect the .packages file in parent directory. - // That file is empty, so if it is used, the system cannot resolve "foo". - var files = { - "sub": {"packages": fooPackage}, - ".packages": "" - }; - addScheme( - "implicit packages dir overrides parent .packages", "%$scheme/sub/", - files: files, - expect: { - "iroot": "%$scheme/sub/packages/", - "foo": "%$scheme/sub/packages/foo", - "foo/": "%$scheme/sub/packages/foo/", - "foo/bar": "%$scheme/sub/packages/foo/bar", - // "foo.x": "qux", // Blocked by issue http://dartbug.com/26482 - "bar/bar": "%$scheme/sub/packages/bar/bar", - }); - } - - { - // No parameters, a .packages file next to entry is found and used. - // A packages/ directory is ignored. - var files = { - ".packages": "foo:pkgs/foo/", - "packages": {}, - "pkgs": fooPackage - }; - addScheme("implicit .packages file", "%$scheme/", files: files, expect: { - "iconf": "%$scheme/.packages", - "foo/": "%$scheme/pkgs/foo/", - "foo/bar": "%$scheme/pkgs/foo/bar", - }); - } - - { - // No parameters, a .packages file in parent dir, no packages/ dir. - // With a file: URI, find the .packages file. - // WIth a http: URI, assume a packages/ dir. - var files = {"sub": {}, ".packages": "foo:pkgs/foo/", "pkgs": fooPackage}; - addScheme(".packages file in parent", "%$scheme/sub/", - files: files, - expect: (scheme == "file") - ? { - "iconf": "%file/.packages", - "foo/": "%file/pkgs/foo/", - "foo/bar": "%file/pkgs/foo/bar", - } - : { - "iroot": "%http/sub/packages/", - "foo": "%http/sub/packages/foo", - "foo/": "%http/sub/packages/foo/", - "foo/bar": "%http/sub/packages/foo/bar", - "foo.x": null, - "bar/bar": "%http/sub/packages/bar/bar", - }); - } - - { - // Specified package root that doesn't exist. - // Ignores existing .packages file and packages/ dir. - addScheme("explicit root not there", "%$scheme/", - files: { - "packages": fooPackage, - ".packages": "foo:%$scheme/packages/" - }, - root: "%$scheme/notthere/", - expect: { - "proot": "%$scheme/notthere/", - "iroot": "%$scheme/notthere/", - "foo": "%$scheme/notthere/foo", - "foo/": "%$scheme/notthere/foo/", - "foo/bar": "%$scheme/notthere/foo/bar", - "foo.x": null, - "bar/bar": "%$scheme/notthere/bar/bar", - }); - } - - { - // Specified package config that doesn't exist. - // Ignores existing .packages file and packages/ dir. - addScheme("explicit config not there", "%$scheme/", - files: {".packages": "foo:packages/foo/", "packages": fooPackage}, - config: "%$scheme/.notthere", - expect: { - "pconf": "%$scheme/.notthere", - "iconf": null, // <- Only there if actually loaded (unspecified). - "foo/": null, - "foo/bar": null, - "foo.x": null, - }); - } - - { - // Specified package root with no trailing slash. - // The Platform.packageRoot and Isolate.packageRoot has a trailing slash. - var files = { - ".packages": "foo:packages/foo/", - "packages": {}, - "pkgs": fooPackage - }; - addScheme("explicit package root, no slash", "%$scheme/", - files: files, - root: "%$scheme/pkgs", - expect: { - "proot": "%$scheme/pkgs/", - "iroot": "%$scheme/pkgs/", - "foo": "%$scheme/pkgs/foo", - "foo/": "%$scheme/pkgs/foo/", - "foo/bar": "%$scheme/pkgs/foo/bar", - "bar/bar": "%$scheme/pkgs/bar/bar", - }); - } - - { - // Specified package root with trailing slash. - var files = { - ".packages": "foo:packages/foo/", - "packages": {}, - "pkgs": fooPackage - }; - addScheme("explicit package root, slash", "%$scheme/", - files: files, - root: "%$scheme/pkgs", - expect: { - "proot": "%$scheme/pkgs/", - "iroot": "%$scheme/pkgs/", - "foo": "%$scheme/pkgs/foo", - "foo/": "%$scheme/pkgs/foo/", - "foo/bar": "%$scheme/pkgs/foo/bar", - "bar/bar": "%$scheme/pkgs/bar/bar", - }); - } - - { - // Specified package config. - var files = { - ".packages": "foo:packages/foo/", - "packages": {}, - ".pkgs": "foo:pkgs/foo/", - "pkgs": fooPackage - }; - addScheme("explicit package config file", "%$scheme/", - files: files, - config: "%$scheme/.pkgs", - expect: { - "pconf": "%$scheme/.pkgs", - "iconf": "%$scheme/.pkgs", - "foo/": "%$scheme/pkgs/foo/", - "foo/bar": "%$scheme/pkgs/foo/bar", - }); - } - - { - // Specified package config as data: URI. - // The package config can be specified as a data: URI. - // (In that case, relative URI references in the config file won't work). - var files = { - ".packages": "foo:packages/foo/", - "packages": {}, - "pkgs": fooPackage - }; - var dataUri = "data:,foo:%$scheme/pkgs/foo/\n"; - addScheme("explicit data: config file", "%$scheme/", - files: files, - config: dataUri, - expect: { - "pconf": dataUri, - "iconf": dataUri, - "foo/": "%$scheme/pkgs/foo/", - "foo/bar": "%$scheme/pkgs/foo/bar", - }); - } - } - - // Tests where there are files on both http: and file: sources. - - for (var entryScheme in const ["file", "http"]) { - for (var pkgScheme in const ["file", "http"]) { - // Package root. - if (entryScheme != pkgScheme) { - // Package dir and entry point on different schemes. - var files = {}; - var https = {}; - (entryScheme == "file" ? files : https)["main"] = testMain; - (pkgScheme == "file" ? files : https)["pkgs"] = fooPackage; - add("$pkgScheme pkg/$entryScheme main", "%$entryScheme/", - file: files, - http: https, - root: "%$pkgScheme/pkgs/", - expect: { - "proot": "%$pkgScheme/pkgs/", - "iroot": "%$pkgScheme/pkgs/", - "foo": "%$pkgScheme/pkgs/foo", - "foo/": "%$pkgScheme/pkgs/foo/", - "foo/bar": "%$pkgScheme/pkgs/foo/bar", - "bar/bar": "%$pkgScheme/pkgs/bar/bar", - "foo.x": "qux", - }); - } - // Package config. The configuration file may also be on either source. - for (var configScheme in const ["file", "http"]) { - // Don't do the boring stuff! - if (entryScheme == configScheme && entryScheme == pkgScheme) continue; - // Package config, packages and entry point not all on same scheme. - var files = {}; - var https = {}; - (entryScheme == "file" ? files : https)["main"] = testMain; - (configScheme == "file" ? files : https)[".pkgs"] = - "foo:%$pkgScheme/pkgs/foo/\n"; - (pkgScheme == "file" ? files : https)["pkgs"] = fooPackage; - add("$pkgScheme pkg/$configScheme config/$entryScheme main", - "%$entryScheme/", - file: files, - http: https, - config: "%$configScheme/.pkgs", - expect: { - "pconf": "%$configScheme/.pkgs", - "iconf": "%$configScheme/.pkgs", - "foo/": "%$pkgScheme/pkgs/foo/", - "foo/bar": "%$pkgScheme/pkgs/foo/bar", - "foo.x": "qux", - }); - } - } - } -} - -// --------------------------------------------------------- -// Helper functionality. - -var fileHttpRegexp = new RegExp(r"%(?:file|http)/"); - -// Executes a test in a configuration. -// -// The test must specify which main file to use -// (`main`, `spawnMain` or `spawnUriMain`) -// and any arguments which will be used by `spawnMain` and `spawnUriMain`. -// -// The [expect] map may be used to override the expectations of the -// configuration on a value-by-value basis. Passing, e.g., `{"pconf": null}` -// will override only the `pconf` (`Platform.packageConfig`) expectation. -Future testConfiguration(Configuration conf) async { - print("-- ${conf.description}"); - var description = conf.description; - try { - var output = await execDart(conf.mainFile, - root: conf.root, config: conf.config, scriptArgs: conf.args); - match(json.decode(output), conf.expect, description, output); - } catch (e, s) { - // Unexpected error calling execDart or parsing the result. - // Report it and continue. - print("ERROR running $description: $e\n$s"); - failingTests.putIfAbsent(description, () => []).add("$e"); - } -} - -/// Test that the output of running testMain matches the expectations. -/// -/// The output is a string which is parse as a JSON literal. -/// The resulting map is always mapping strings to strings, or possibly `null`. -/// The expectations can have non-string values other than null, -/// they are `toString`'ed before being compared (so the caller can use a URI -/// or a File/Directory directly as an expectation). -void match(Map actuals, Map expectations, String desc, String actualJson) { - for (var key in expectations.keys) { - var expectation = expectations[key]?.toString(); - var actual = actuals[key]; - if (expectation != actual) { - print("ERROR: $desc: $key: Expected: <$expectation> Found: <$actual>"); - failingTests - .putIfAbsent(desc, () => []) - .add("$key: $expectation != $actual"); - } - } -} - -const String improt = "import"; // Avoid multitest import rewriting. - -/// Script that prints the current state and the result of resolving -/// a few package URIs. This script will be invoked in different settings, -/// and the result will be parsed and compared to the expectations. -const String testMain = """ -$improt "dart:convert" show json; -$improt "dart:io" show Platform, Directory; -$improt "dart:isolate" show Isolate; -$improt "package:foo/foo.dart" deferred as foo; -main(_) async { - String platformRoot = await Platform.packageRoot; - String platformConfig = await Platform.packageConfig; - Directory cwd = Directory.current; - Uri script = Platform.script; - Uri isolateRoot = await Isolate.packageRoot; - Uri isolateConfig = await Isolate.packageConfig; - Uri base = Uri.base; - Uri res1 = await Isolate.resolvePackageUri(Uri.parse("package:foo")); - Uri res2 = await Isolate.resolvePackageUri(Uri.parse("package:foo/")); - Uri res3 = await Isolate.resolvePackageUri(Uri.parse("package:foo/bar")); - Uri res4 = await Isolate.resolvePackageUri(Uri.parse("package:bar/bar")); - Uri res5 = await Isolate.resolvePackageUri(Uri.parse("relative/path")); - Uri res6 = await Isolate.resolvePackageUri( - Uri.parse("http://example.org/file")); - String fooX = await foo - .loadLibrary() - .timeout(const Duration(seconds: 1)) - .then((_) => foo.x, onError: (_) => null); - print(json.encode({ - "cwd": cwd.path, - "base": base?.toString(), - "script": script?.toString(), - "proot": platformRoot, - "pconf": platformConfig, - "iroot" : isolateRoot?.toString(), - "iconf" : isolateConfig?.toString(), - "foo": res1?.toString(), - "foo/": res2?.toString(), - "foo/bar": res3?.toString(), - "foo.x": fooX?.toString(), - "bar/bar": res4?.toString(), - "relative": res5?.toString(), - "nonpkg": res6?.toString(), - })); -} -"""; - -/// Script that spawns a new Isolate using Isolate.spawnUri. -/// -/// Takes URI of target isolate, package config, package root and -/// automatic package resolution-flag parameters as command line arguments. -/// Any further arguments are forwarded to the spawned isolate. -const String spawnUriMain = """ -$improt "dart:isolate"; -$improt "dart:async"; -main(args) async { - Uri target = Uri.parse(args[0]); - Uri config = (args[1] == "-") ? null : Uri.parse(args[1]); - Uri root = (args[2] == "-") ? null : Uri.parse(args[2]); - bool search = args[3] == "true"; - var restArgs = args.skip(4).toList(); - // Port keeps isolate alive until spawned isolate terminates. - var port = new RawReceivePort(); - port.handler = (res) async { - port.close(); // Close on exit or first error. - if (res != null) { - await new Future.error(res[0], new StackTrace.fromString(res[1])); - } - }; - Isolate.spawnUri(target, restArgs, null, - packageRoot: root, packageConfig: config, - automaticPackageResolution: search, - onError: port.sendPort, onExit: port.sendPort); -} -"""; - -/// Script that spawns a new Isolate using Isolate.spawn. -/// -/// Uses the first argument to select which target to spawn. -/// Should be either "test", "uri" or "spawn". -const String spawnMain = """ -$improt "dart:async"; -$improt "dart:isolate"; -$improt "%mainDir/main.dart" as test; -$improt "%mainDir/spawnUriMain.dart" as spawnUri; -main(List args) async { - // Port keeps isolate alive until spawned isolate terminates. - var port = new RawReceivePort(); - port.handler = (res) async { - port.close(); // Close on exit or first error. - if (res != null) { - await new Future.error(res[0], new StackTrace.fromString(res[1])); - } - }; - var arg = args.first; - var rest = args.skip(1).toList(); - var target; - if (arg == "main") { - target = test.main; - } else if (arg == "spawnUriMain") { - target = spawnUri.main; - } else { - target = main; - } - Isolate.spawn(target, rest, onError: port.sendPort, onExit: port.sendPort); -} -"""; - -/// A package directory containing only one package, "foo", with one file. -const Map fooPackage = const { - "foo": const {"foo": "var x = 'qux';"} -}; - -/// Runs the Dart executable with the provided parameters. -/// -/// Captures and returns the output. -Future execDart(String script, - {String root, String config, Iterable scriptArgs}) async { - var checked = false; - assert((checked = true)); - // TODO: Find a way to change CWD before running script. - var executable = Platform.executable; - var args = []; - if (checked) args.add("--checked"); - if (root != null) args.add("--package-root=$root"); - if (config != null) args.add("--packages=$config"); - args.add(script); - if (scriptArgs != null) { - args.addAll(scriptArgs); - } - return Process.run(executable, args).then((results) { - if (results.exitCode != 0 || results.stderr.isNotEmpty) { - throw results.stderr; - } - return results.stdout; - }); -} - -/// Creates a number of files and subdirectories. -/// -/// The [content] is the content of the directory itself. The map keys are -/// names and the values are either strings that represent Dart file contents -/// or maps that represent subdirectories. -void createFiles(Directory tempDir, String subDir, Map content) { - Directory createDir(Directory base, String name) { - Directory newDir = new Directory(p.join(base.path, name)); - newDir.createSync(); - return newDir; - } - - void createTextFile(Directory base, String name, String content) { - File newFile = new File(p.join(base.path, name)); - newFile.writeAsStringSync(content); - } - - void createRecursive(Directory dir, Map map) { - for (var name in map.keys) { - var content = map[name]; - if (content is String) { - // If the name starts with "." it's a .packages file, otherwise it's - // a dart file. Those are the only files we care about in this test. - createTextFile( - dir, name.startsWith(".") ? name : name + ".dart", content); - } else { - assert(content is Map); - var subdir = createDir(dir, name); - createRecursive(subdir, content); - } - } - } - - createRecursive(createDir(tempDir, subDir), content); -} - -/// Start an HTTP server which serves a directory/file structure. -/// -/// The directories and files are described by [files]. -/// -/// Each map key is an entry in a directory. A `Map` value is a sub-directory -/// and a `String` value is a text file. -/// The file contents are run through [fixPaths] to allow them to be self- -/// referential. -Future startServer(Map files) async { - return (await HttpServer.bind(InternetAddress.loopbackIPv4, 0)) - ..forEach((request) { - var result = files; - onFailure: - { - for (var part in request.uri.pathSegments) { - if (part.endsWith(".dart")) { - part = part.substring(0, part.length - 5); - } - if (result is Map) { - result = result[part]; - } else { - break onFailure; - } - } - if (result is String) { - request.response - ..write(result) - ..close(); - return; - } - } - request.response - ..statusCode = HttpStatus.notFound - ..close(); - }); -} - -// Counter used to avoid reusing temporary file or directory names. -// -// Used when adding extra files to an existing directory structure, -// and when creating temporary directories. -// -// Some platform temporary-directory implementations are timer based, -// and creating two temp-dirs withing a short duration may cause a collision. -int tmpNameCounter = 0; - -// Fresh file name. -String freshName([String base = "tmp"]) => "$base${tmpNameCounter++}"; - -Directory createTempDir() { - return Directory.systemTemp.createTempSync(freshName("pftest-")); -} - -typedef void ConfigUpdate(Configuration configuration); - -/// The configuration for a single test. -class Configuration { - /// The "description" of the test - a description of the set-up. - final String description; - - /// The package root parameter passed to the Dart isolate. - /// - /// At most one of [root] and [config] should be supplied. If both are - /// omitted, a VM will search for a packages file or dir. - final String root; - - /// The package configuration file location passed to the Dart isolate. - final String config; - - /// Path to the main file to run. - final String mainFile; - - /// List of arguments to pass to the main function. - final List args; - - /// The expected values for `Platform.package{Root,Config}`, - /// `Isolate.package{Root,Config}` and resolution of package URIs - /// in a `foo` package. - /// - /// The results are found by running the `main.dart` file inside [mainDir]. - /// The tests can run this file after doing other `spawn` or `spawnUri` calls. - final Map expect; - - Configuration( - {this.description, - this.root, - this.config, - this.mainFile, - this.args, - this.expect}); - - // Gets the type of main file, one of `main`, `spawnMain` or `spawnUriMain`. - String get mainType { - var lastSlash = mainFile.lastIndexOf("/"); - if (lastSlash < 0) { - // Assume it's a Windows path. - lastSlash = mainFile.lastIndexOf(r"\"); - } - var name = mainFile.substring(lastSlash + 1, mainFile.length - 5); - assert(name == "main" || name == "spawnMain" || name == "spawnUriMain"); - return name; - } - - String get mainPath { - var lastSlash = mainFile.lastIndexOf("/"); - if (lastSlash < 0) { - // Assume it's a Windows path. - lastSlash = mainFile.lastIndexOf(r"\"); - } - return mainFile.substring(0, lastSlash + 1); - } - - /// Create a new configuration from the old one. - /// - /// [description] is new description. - /// - /// [main] is one of `main`, `spawnMain` or `spawnUriMain`, and changes - /// the [Configuration.mainFile] to a different file in the same directory. - /// - /// [mainFile] overrides [Configuration.mainFile] completely, and ignores - /// [main]. - /// - /// [newArgs] are prepended to the existing [Configuration.args]. - /// - /// [args] overrides [Configuration.args] completely and ignores [newArgs]. - /// - /// [expect] overrides individual expectations. - /// - /// [root] and [config] overrides the existing values. - Configuration update( - {String description, - String main, - String mainFile, - String root, - String config, - List args, - List newArgs, - Map expect}) { - return new Configuration( - description: description ?? this.description, - root: root ?? this.root, - config: config ?? this.config, - mainFile: mainFile ?? - ((main == null) ? this.mainFile : "${this.mainPath}$main.dart"), - args: args ?? - ([] - ..addAll(newArgs ?? const []) - ..addAll(this.args)), - expect: expect == null ? this.expect : new Map.from(this.expect) - ..addAll(expect ?? const {})); - } - - // For debugging. - String toString() { - return "Configuration($description\n" - " root : $root\n" - " config: $config\n" - " main : $mainFile\n" - " args : ${args.map((x) => '"$x"').join(" ")}\n" - ") : expect {\n${expect.keys.map((k) => ' "$k"'.padRight(6) + ":${json.encode(expect[k])}\n").join()}" - "}"; - } -} - -// Inserts the file with generalized [name] at [path] with [content]. -// -// The [path] is a directory where the file is created. It must start with -// either '%file/' or '%http/' to select the structure to put it into. -// -// The [name] should not have a trailing ".dart" for Dart files. Any file -// not starting with "." is assumed to be a ".dart" file. -void insertFileAt( - Map file, Map http, String path, String name, String content) { - var parts = path.split('/').toList(); - var dir = (parts[0] == "%file") ? file : http; - for (var i = 1; i < parts.length - 1; i++) { - var entry = parts[i]; - dir = dir[entry] ?? (dir[entry] = {}); - } - dir[name] = content; -} diff --git a/tests/standalone_2/standalone_2.status b/tests/standalone_2/standalone_2.status index 4edeb994a2f..09fccf4d466 100644 --- a/tests/standalone_2/standalone_2.status +++ b/tests/standalone_2/standalone_2.status @@ -11,8 +11,6 @@ io/large_file_read_small_file_test: Slow, Pass # Test reads small file 1M times io/non_utf8_directory_test: Skip # Issue 33519. Temp files causing bots to go purple. io/non_utf8_file_test: Skip # Issue 33519. Temp files causing bots to go purple. io/non_utf8_link_test: Skip # Issue 33519. Temp files causing bots to go purple. -packages_file_test: Skip # Issue 26715 -packages_file_test/none: Skip # contains no tests. [ $builder_tag == dwarf ] io/socket_connect_stacktrace_test: SkipByDesign # Assumes stacktrace can be inspected directly, without decoding