Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(615)

Unified Diff: tests/standalone/packages_file_test.dart

Issue 1993643002: Add test of some package-config configurations. (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Fix type Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tests/standalone/packages_file_test.dart
diff --git a/tests/standalone/packages_file_test.dart b/tests/standalone/packages_file_test.dart
new file mode 100644
index 0000000000000000000000000000000000000000..299c4fa251dadd41be7133cda54f67caad2e4907
--- /dev/null
+++ b/tests/standalone/packages_file_test.dart
@@ -0,0 +1,432 @@
+// 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:isolate";
+import "dart:convert" show JSON;
+import "package:path/path.dart" as p;
+import "package:expect/expect.dart";
+import "package:async_helper/async_helper.dart";
+
+main() async {
+ asyncStart();
+
+ for (var test in [testDirect]) {
floitsch 2016/05/18 13:36:32 As long as there is just one, I would just rename
+ await test("file: no resolution", (run, dir) async {
+ // Expect everything to be null.
+ await run(dir.resolve("main.dart"), {"foo.x": null});
floitsch 2016/05/18 13:36:32 This ping-pong is too complicated. Just add a tok
Lasse Reichstein Nielsen 2016/05/18 14:01:16 And I haven't even gotten started on nested calls
+ }, files: {"main": testMain});
+
+ await test("http: no resolution", (run, dir) async {
+ // An HTTP script with no ".packages" file assumes a "packages" dir.
+ await run(dir.resolve("main.dart"), {
+ // "foo": null,
+ "foo/": dir.resolve("packages/foo/"),
+ "foo/bar": dir.resolve("packages/foo/bar"),
+ "foo.x": null,
+ });
+ }, http: {"main": testMain});
+
+ for (var scheme in ["file", "http"]) {
+ {
+ var files = {"main": testMain, "packages": fooPackage};
+ await test("$scheme: implicit packages dir", (run, dir) async {
+ // Expect implicitly detected package dir.
+ await run(dir.resolve("main.dart"), {
+ "iroot": dir.resolve("packages/"),
+ // "foo": null,
+ "foo/": dir.resolve("packages/foo/"),
+ "foo/bar": dir.resolve("packages/foo/bar"),
+ });
+ }, files: scheme == "file" ? files : null,
+ http: scheme == "http" ? files : null);
+ }
+
+ {
+ var files = {"sub": {"main": testMain, "packages": fooPackage},
+ ".packages": ""};
+ await test("$scheme: implicit packages dir 2", (run, dir) async {
+ // Expect implicitly detected package dir.
+ await run(dir.resolve("sub/main.dart"), {
+ "iroot": dir.resolve("sub/packages/"),
+ // "foo": null,
+ "foo/": dir.resolve("sub/packages/foo/"),
+ "foo/bar": dir.resolve("sub/packages/foo/bar"),
+ });
+ }, files: scheme == "file" ? files : null,
+ http: scheme == "http" ? files : null);
+ }
+
+ {
+ var files = {"main": testMain,
+ ".packages": "foo:pkgs/foo/",
+ "pkgs": fooPackage};
+ await test("$scheme: implicit .packages file", (run, dir) async {
+ // Expect implicitly detected .package file.
+ await run(dir.resolve("main.dart"), {
+ "iconf": dir.resolve(".packages"),
+ // "foo": null,
+ "foo/": dir.resolve("pkgs/foo/"),
+ "foo/bar": dir.resolve("pkgs/foo/bar"),
+ });
+ }, files: scheme == "file" ? files : null,
+ http: scheme == "http" ? files : null);
+ }
+
+ {
+ var files = {"main": testMain,
+ ".packages": "foo:packages/foo/",
+ "packages": fooPackage,
+ "pkgs": fooPackage};
+ await test("$scheme: explicit package root, no slash", (run, dir) async {
floitsch 2016/05/18 13:36:32 long line.
+ await run(dir.resolve("main.dart"), {
+ "proot": dir.resolve("pkgs/"),
+ "iroot": dir.resolve("pkgs/"),
+ // "foo": null,
+ "foo/": dir.resolve("pkgs/foo/"),
+ "foo/bar": dir.resolve("pkgs/foo/bar"),
+ }, root: dir.resolve("pkgs"));
+ }, files: scheme == "file" ? files : null,
+ http: scheme == "http" ? files : null);
+
+ await test("$scheme: explicit package root, slash", (run, dir) async {
+ await run(dir.resolve("main.dart"), {
+ "proot": dir.resolve("pkgs/"),
+ "iroot": dir.resolve("pkgs/"),
+ // "foo": null,
+ "foo/": dir.resolve("pkgs/foo/"),
+ "foo/bar": dir.resolve("pkgs/foo/bar"),
+ }, root: dir.resolve("pkgs/"));
+ }, files: scheme == "file" ? files : null,
+ http: scheme == "http" ? files : null);
+ }
+
+ {
+ var files = {"main": testMain,
+ ".packages": "foo:packages/foo/",
+ "packages": fooPackage,
+ ".pkgs": "foo:pkgs/foo/",
+ "pkgs": fooPackage};
+ await test("$scheme: explicit package config file", (run, dir) async {
+ await run(dir.resolve("main.dart"), {
+ "pconf": dir.resolve(".pkgs"),
+ "iconf": dir.resolve(".pkgs"),
+ // "foo": null,
+ "foo/": dir.resolve("pkgs/foo/"),
+ "foo/bar": dir.resolve("pkgs/foo/bar"),
+ }, config: dir.resolve(".pkgs"));
+ }, files: scheme == "file" ? files : null,
+ http: scheme == "http" ? files : null);
+ }
+
+ {
+ var files = {"main": testMain,
+ ".packages": "foo:packages/foo/",
+ "packages": fooPackage,
+ "pkgs": fooPackage};
+ await test("$scheme: explicit data: config file", (run, dir) async {
+ var dataUri = new UriData.fromString(
+ "foo:${dir.resolve("pkgs/foo/")}").uri;
+ await run(dir.resolve("main.dart"), {
+ "pconf": dataUri,
+ "iconf": dataUri,
+ // "foo": null,
+ "foo/": dir.resolve("pkgs/foo/"),
+ "foo/bar": dir.resolve("pkgs/foo/bar"),
+ }, config: dataUri);
+ }, files: scheme == "file" ? files : null,
+ http: scheme == "http" ? files : null);
+ }
+ }
+
+ {
+ // With a file: URI, the lookup checks for a .packages file in superdirs.
+ var files = {"sub": { "main": testMain },
+ ".packages": "foo:pkgs/foo/",
+ "pkgs": fooPackage};
+ await test("file: implicit .packages file in ..", (run, dir) async {
+ // Expect implicitly detected .package file.
+ await run(dir.resolve("sub/main.dart"), {
+ "iconf": dir.resolve(".packages"),
+ // "foo": null,
+ "foo/": dir.resolve("pkgs/foo/"),
+ "foo/bar": dir.resolve("pkgs/foo/bar"),
+ });
+ }, files: files);
+ }
+
+ {
+ // With a non-file: URI, the lookup assumes a packges/ dir.
+ var files = {"sub": { "main": testMain },
+ ".packages": "foo:pkgs/foo/",
+ "pkgs": fooPackage};
+ await test("http: implicit packages dir", (run, dir) async {
+ // Expect implicitly detected .package file.
+ await run(dir.resolve("sub/main.dart"), {
+ "iroot": dir.resolve("sub/packages/"),
+ // "foo": null,
+ "foo/": dir.resolve("sub/packages/foo/"),
+ "foo/bar": dir.resolve("sub/packages/foo/bar"),
+ "foo.x": null,
+ });
+ }, http: files);
+ }
+ }
+
+
+ if (failingTests.isNotEmpty) {
+ print("Errors found in tests:\n ${failingTests.join("\n ")}\n");
+ exit(255);
+ }
+ asyncEnd();
+}
+
+// ---------------------------------------------------------
+// Helper fnuctionality.
floitsch 2016/05/18 13:36:32 functionality
+
+var failingTests = new Set();
+
+Future testDirect(name, Function test, {Map files, Map http}) async {
+ var tmpDir;
+ var https;
+ if (files != null) tmpDir = createFiles(files);
+ if (http != null) https = await startServer(http);
+ runTest(main, expectations, {Uri root, Uri config}) async {
floitsch 2016/05/18 13:36:32 Please keep one line before and after nested funct
+ var output = await runDart(main, root: root, config: config);
+ // 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.
+ var expects = {
+ "pconf": null,
+ "proot": null,
+ "iconf": null,
+ "iconf": null,
+ // "foo": null,
+ "foo/": null,
+ "foo/bar": null,
+ "foo.x": "qux",
+ }..addAll(expectations);
+ match(JSON.decode(output), expects, name);
+ }
+ // Call test with 1-3 parameters depending on which setups were made.
+ try {
+ if (https == null) {
+ if (files == null) {
+ await test(runTest); // Data-schemes only, no files or http available.
+ } else {
+ await test(runTest, tmpDir.uri);
+ }
+ } else if (files == null) {
+ await test(runTest, serverUri(https));
+ } else {
+ await test(runTest, tmpDir.uri, serverUri(https));
floitsch 2016/05/18 13:36:32 You don't seem to use this case.
Lasse Reichstein Nielsen 2016/05/18 14:01:16 Yet :) I need to have cases where the script is fi
+ }
+ } catch (e) {
+ print("ERROR in $name: $e");
floitsch 2016/05/18 13:36:32 I would rethrow.
Lasse Reichstein Nielsen 2016/05/18 14:01:16 Whoops, left over debug print.
+ } finally {
+ if (https != null) await https.close();
+ if (tmpDir != null) tmpDir.deleteSync(recursive: true);
+ }
+}
+
+
+
+/// 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 name) {
+ for (var key in expectations.keys) {
+ var expectation = expectations[key];
+ var actual = actuals[key];
+ if (expectation?.toString() != actual) {
+ print("ERROR: $name: $key: Expected: <$expectation> Found: <$actual>");
+ failingTests.add(name);
+ }
+ }
+}
+
+/// 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 = r"""
+import "dart:convert" show JSON;
+import "dart:io" show Platform, Directory;
+import "dart:isolate" show Isolate;
+import "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"));
+ 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(),
floitsch 2016/05/18 13:36:32 No need for "?.". Null supports 'toString' too.
Lasse Reichstein Nielsen 2016/05/18 14:01:16 But that would make at the string "null", not the
floitsch 2016/05/19 12:19:23 right.
+ "foo/": res2?.toString(),
+ "foo/bar": res3?.toString(),
+ "foo.x": fooX?.toString(),
+ }));
+}
+""";
+
+/// Script that spawns a new Isolate using Isolate.spawnUri.
+///
+/// Takes URI of target isolate, package config and package root as
+/// command line arguments. Any further arguments are forwarded to the
+/// spawned isolate.
+const String spawnUriMain = r"""
+import "dart:isolate";
+main(args) async {
+ Uri target = Uri.parse(args[0]);
+ Uri conf = args.length > 1 && args[1].isNotEmpty ? Uri.parse(args[1]) : null;
+ Uri root = args.length > 2 && args[2].isNotEmpty ? Uri.parse(args[2]) : null;
+ var restArgs = args.skip(3).toList();
+ var isolate = await Isolate.spawnUri(target, restArgs,
+ packageRoot: root, packageConfig: conf, paused: true);
+ // Wait for isolate to exit before exiting the main isolate.
+ var done = new RawReceivePort();
+ done.handler = (_) { done.close(); };
+ isolate.addExitHandler(done.sendPort);
+ isolate.resume(isolate.pauseCapability);
+}
+""";
+
+/// Script that spawns a new Isolate using Isolate.spawn.
+const String spawnMain = r"""
+import "dart:isolate";
+import "testmain.dart" as test;
+main() async {
+ var isolate = await Isolate.spawn(test.main, [], paused: true);
+ // Wait for isolate to exit before exiting the main isolate.
+ var done = new RawReceivePort();
+ done.handler = (_) { done.close(); };
+ isolate.addExitHandler(done.sendPort);
+ isolate.resume(isolate.pauseCapability);
+}
+""";
+
+/// 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.
+///
+/// Optionally sets CWD before running the script.
+/// Captures and returns the output.
+Future<String> runDart(Uri script,
+ {Uri root, Uri config, List<String> scriptArgs}) async {
+ var executable = Platform.executable;
+ var args = [];
+ if (root != null) args..add("-p")..add(root.toString());
+ if (config != null) args..add("--packages=$config");
+ args.add(script.toString());
+ args.addAll(scriptArgs ?? []);
+ return Process.run(executable, args).then((results) {
+ if (results.exitCode != 0) {
+ throw results.stderr;
+ }
+ return results.stdout;
+ });
+}
+
+/// Creates a temporary directory and 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.
+/// Subdirectories may include a package directory. If [packageDir]
+/// is provided, a `.packages` file is created for the content of that
+/// directory.
+Directory createFiles(Map content,
+ [String packageDir]) {
+ Directory createDir(Directory base, String name) {
floitsch 2016/05/18 13:36:32 New lines before and after nested functions.
+ 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 create(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 {
+ var subdir = createDir(dir, name);
+ create(subdir, content);
+ }
+ }
+ }
+ var tempDir = Directory.systemTemp.createTempSync("pftest-${tmpDirCounter++}-");
floitsch 2016/05/18 13:36:32 long line.
+ create(tempDir, content);
+ if (packageDir != null) {
+ Map packages = content[packageDir];
+ var entries =
+ packages.keys.map((key) => "$key:$packageDir/$key").join("\n");
+ createTextFile(tempDir, ".packages", entries);
+ }
+ return tempDir;
+}
+
+// Counter used to avoid reusing temporary directory names.
+int tmpDirCounter = 0;
+
+Future<HttpServer> startServer(Map files) async {
+ return (await HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, 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.NOT_FOUND
+ ..close();
+ });
+}
+
+Uri serverUri(HttpServer server) =>
+ new Uri(scheme: "http",
+ host: server.address.address,
+ port: server.port,
+ path: "/");
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698