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

Side by Side 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: Address comments 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 unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 import "dart:async";
6 import "dart:io";
7 import "dart:isolate";
8 import "dart:convert" show JSON;
9 import "package:path/path.dart" as p;
10 import "package:expect/expect.dart";
11 import "package:async_helper/async_helper.dart";
12
13 main() async {
14 asyncStart();
15
16 await test("file: no resolution",
floitsch 2016/05/26 19:09:55 Add a comment on the first test that explains that
17 "%file/main.dart",
18 file: {"main": testMain},
19 expect: {"foo.x": null});
20
21 // An HTTP script with no ".packages" file assumes a "packages" dir.
22 await test("http: no resolution", "%http/main.dart",
23 http: {"main": testMain},
24 expect: {
25 // "foo": null,
26 "foo/": "%http/packages/foo/",
27 "foo/bar": "%http/packages/foo/bar",
28 "foo.x": null,
29 });
30
31 for (var scheme in ["file", "http"]) {
32
33 testScheme(name, main, {expect, files, args, root, config}) {
34 return test("$scheme: $name", main, expect: expect,
35 root: root, config: config, args: args,
36 file: scheme == "file" ? files : null,
37 http: scheme == "http" ? files : null);
38 }
39
40 {
41 var files = {"main": testMain, "packages": fooPackage};
42 // Expect implicitly detected package dir.
43 await testScheme("implicit packages dir","%$scheme/main.dart",
44 files: files,
45 expect: {
46 "iroot": "%$scheme/packages/",
47 // "foo": null,
48 "foo/": "%$scheme/packages/foo/",
49 "foo/bar": "%$scheme/packages/foo/bar",
50 });
51 }
52
53 {
54 var files = {"sub": {"main": testMain, "packages": fooPackage},
55 ".packages": ""};
floitsch 2016/05/26 19:09:56 Provide comment what an empty ".packages" should d
Lasse Reichstein Nielsen 2016/05/27 10:53:56 Will do. We don't ignore it - the test should not
56 // Expect implicitly detected package dir.
57 await testScheme("implicit packages dir 2", "%$scheme/sub/main.dart",
58 files: files,
59 expect: {
60 "iroot": "%$scheme/sub/packages/",
61 // "foo": null,
62 "foo/": "%$scheme/sub/packages/foo/",
63 "foo/bar": "%$scheme/sub/packages/foo/bar",
64 });
65 }
66
67 {
68 var files = {"main": testMain,
69 ".packages": "foo:pkgs/foo/",
70 "pkgs": fooPackage};
71 await testScheme("implicit .packages file", "%$scheme/main.dart",
72 files: files,
73 expect: {
74 "iconf": "%$scheme/.packages",
75 // "foo": null,
76 "foo/": "%$scheme/pkgs/foo/",
77 "foo/bar": "%$scheme/pkgs/foo/bar",
78 });
79 }
80
81 {
82 var files = {"main": testMain,
83 ".packages": "foo:packages/foo/",
84 "packages": fooPackage,
85 "pkgs": fooPackage};
86 await testScheme("explicit package root, no slash", "%$scheme/main.dart",
87 files: files,
88 root: "%$scheme/pkgs",
89 expect: {
90 "proot": "%$scheme/pkgs/",
91 "iroot": "%$scheme/pkgs/",
92 // "foo": null,
93 "foo/": "%$scheme/pkgs/foo/",
94 "foo/bar": "%$scheme/pkgs/foo/bar",
95 });
96 }
97
98 {
99 var files = {"main": testMain,
100 ".packages": "foo:packages/foo/",
101 "packages": fooPackage,
102 "pkgs": fooPackage};
103 await testScheme("explicit package root, slash", "%$scheme/main.dart",
104 files: files,
105 root: "%$scheme/pkgs",
106 expect: {
107 "proot": "%$scheme/pkgs/",
108 "iroot": "%$scheme/pkgs/",
109 // "foo": null,
110 "foo/": "%$scheme/pkgs/foo/",
111 "foo/bar": "%$scheme/pkgs/foo/bar",
112 });
113 }
114
115 {
116 var files = {"main": testMain,
117 ".packages": "foo:packages/foo/",
118 "packages": fooPackage,
119 ".pkgs": "foo:pkgs/foo/",
120 "pkgs": fooPackage};
121 await testScheme("explicit package config file", "%$scheme/main.dart",
122 files: files,
123 config: "%$scheme/.pkgs",
124 expect: {
125 "pconf": "%$scheme/.pkgs",
126 "iconf": "%$scheme/.pkgs",
127 // "foo": null,
128 "foo/": "%$scheme/pkgs/foo/",
129 "foo/bar": "%$scheme/pkgs/foo/bar",
130 });
131 }
132
133 {
134 var files = {"main": testMain,
135 ".packages": "foo:packages/foo/",
136 "packages": fooPackage,
137 "pkgs": fooPackage};
138 var dataUri = "data:,foo:%$scheme/pkgs/foo/\n";
139 await testScheme("explicit data: config file", "%$scheme/main.dart",
140 files: files,
141 config: dataUri,
142 expect: {
143 "pconf": dataUri,
144 "iconf": dataUri,
145 // "foo": null,
146 "foo/": "%$scheme/pkgs/foo/",
147 "foo/bar": "%$scheme/pkgs/foo/bar",
148 });
149 }
150 }
151
152 {
153 // With a file: URI, the lookup checks for a .packages file in superdirs.
154 var files = {"sub": { "main": testMain },
155 ".packages": "foo:pkgs/foo/",
156 "pkgs": fooPackage};
157 await test("file: implicit .packages file in ..", "%file/sub/main.dart",
158 file: files,
159 expect: {
160 "iconf": "%file/.packages",
161 // "foo": null,
162 "foo/": "%file/pkgs/foo/",
163 "foo/bar": "%file/pkgs/foo/bar",
164 });
165 }
166
167 {
168 // With a non-file: URI, the lookup assumes a packges/ dir.
floitsch 2016/05/26 19:09:56 packages
Lasse Reichstein Nielsen 2016/05/27 10:53:56 I like to use .packages to signify the file and pa
169 var files = {"sub": { "main": testMain },
170 ".packages": "foo:pkgs/foo/",
171 "pkgs": fooPackage};
172 // Expect implicitly detected .package file.
173 await test("http: implicit packages dir", "%http/sub/main.dart",
174 http: files,
175 expect: {
176 "iroot": "%http/sub/packages/",
177 // "foo": null,
178 "foo/": "%http/sub/packages/foo/",
179 "foo/bar": "%http/sub/packages/foo/bar",
180 "foo.x": null,
181 });
182 }
183
184
185 if (failingTests.isNotEmpty) {
186 print("Errors found in tests:\n ${failingTests.join("\n ")}\n");
187 exit(255);
188 }
189 asyncEnd();
190 }
191
192 // ---------------------------------------------------------
193 // Helper functionality.
194
195 var failingTests = new Set();
196
197 var fileHttpRegexp = new RegExp(r"%(?:file|http)/");
198
199 Future test(String name, String main,
200 {String root, String config, List<String> args,
201 Map file, Map http, Map expect}) async {
202 String fileRoot = "<no files configured>";
203 String httpRoot = "<not http server configured>";
floitsch 2016/05/26 19:09:56 no
Lasse Reichstein Nielsen 2016/05/27 10:53:56 Will do.
204
205 String fixPaths(String source) {
206 if (source == null) return null;
207 var result = source.replaceAllMapped(fileHttpRegexp, (match) {
208 if (source.startsWith("file", match.start + 1)) return fileRoot;
209 return httpRoot;
210 });
211 return result;
212 }
213
214 Directory tmpDir;
215 var https;
216 if (file != null) {
217 tmpDir = createTempDir();
218 fileRoot = new Uri.directory(tmpDir.path).toString();
219 }
220 if (http != null) {
221 https = await startServer(http, fixPaths);
222 httpRoot = "http://${https.address.address}:${https.port}/";
223 }
224 if (file != null) {
225 createFiles(tmpDir, file, fixPaths);
226 }
227
228 try {
229 var output = await runDart(fixPaths(main),
230 root: fixPaths(root),
231 config: fixPaths(config),
232 scriptArgs: args?.map(fixPaths));
233 // These expectations are default. If not overridden the value will be
234 // expected to be null. That is, you can't avoid testing the actual
235 // value of these, you can only change what value to expect.
236 // For values not included here (commented out), the result is not tested
237 // unless a value (maybe null) is provided.
238 var expects = {
239 "pconf": null,
240 "proot": null,
241 "iconf": null,
242 "iconf": null,
243 // "foo": null,
244 "foo/": null,
245 "foo/bar": null,
246 "foo.x": "qux",
247 }..addAll(expect);
248 match(JSON.decode(output), expects, fixPaths, name);
249 } catch (e) {
250 // Unexpected error calling runDart or parsing the result.
251 // Report it and continue.
252 print("ERROR running $name: $e\n$s");
253 failingTests.add(name);
254 } finally {
255 if (https != null) await https.close();
256 if (tmpDir != null) tmpDir.deleteSync(recursive: true);
257 }
258 }
259
260
261 /// Test that the output of running testMain matches the expectations.
262 ///
263 /// The output is a string which is parse as a JSON literal.
264 /// The resulting map is always mapping strings to strings, or possibly `null`.
265 /// The expectations can have non-string values other than null,
266 /// they are `toString`'ed before being compared (so the caller can use a URI
267 /// or a File/Directory directly as an expectation).
268 void match(Map actuals, Map expectations, String fixPaths(String expectation),
269 String name) {
270 for (var key in expectations.keys) {
271 var expectation = fixPaths(expectations[key]?.toString());
272 var actual = actuals[key];
273 if (expectation != actual) {
274 print("ERROR: $name: $key: Expected: <$expectation> Found: <$actual>");
275 failingTests.add(name);
276 }
277 }
278 }
279
280 /// Script that prints the current state and the result of resolving
281 /// a few package URIs. This script will be invoked in different settings,
282 /// and the result will be parsed and compared to the expectations.
283 const String testMain = r"""
284 import "dart:convert" show JSON;
285 import "dart:io" show Platform, Directory;
286 import "dart:isolate" show Isolate;
287 import "package:foo/foo.dart" deferred as foo;
288 main(_) async {
289 String platformRoot = await Platform.packageRoot;
290 String platformConfig = await Platform.packageConfig;
291 Directory cwd = Directory.current;
292 Uri script = Platform.script;
293 Uri isolateRoot = await Isolate.packageRoot;
294 Uri isolateConfig = await Isolate.packageConfig;
295 Uri base = Uri.base;
296 Uri res1 = await Isolate.resolvePackageUri(Uri.parse("package:foo"));
297 Uri res2 = await Isolate.resolvePackageUri(Uri.parse("package:foo/"));
298 Uri res3 = await Isolate.resolvePackageUri(Uri.parse("package:foo/bar"));
299 String fooX = await foo
300 .loadLibrary()
301 .timeout(const Duration(seconds: 1))
302 .then((_) => foo.x, onError: (_) => null);
303 print(JSON.encode({
304 "cwd": cwd.path,
305 "base": base?.toString(),
306 "script": script?.toString(),
307 "proot": platformRoot,
308 "pconf": platformConfig,
309 "iroot" : isolateRoot?.toString(),
310 "iconf" : isolateConfig?.toString(),
311 "foo": res1?.toString(),
312 "foo/": res2?.toString(),
313 "foo/bar": res3?.toString(),
314 "foo.x": fooX?.toString(),
315 }));
316 }
317 """;
318
319 /// Script that spawns a new Isolate using Isolate.spawnUri.
320 ///
321 /// Takes URI of target isolate, package config and package root as
322 /// command line arguments. Any further arguments are forwarded to the
323 /// spawned isolate.
324 const String spawnUriMain = r"""
floitsch 2016/05/26 19:09:56 unused.
Lasse Reichstein Nielsen 2016/05/27 10:53:56 Yes. Reserved for later use. That's the part I'm g
325 import "dart:isolate";
326 main(args) async {
327 Uri target = Uri.parse(args[0]);
328 Uri conf = args.length > 1 && args[1].isNotEmpty ? Uri.parse(args[1]) : null;
329 Uri root = args.length > 2 && args[2].isNotEmpty ? Uri.parse(args[2]) : null;
330 var restArgs = args.skip(3).toList();
331 var isolate = await Isolate.spawnUri(target, restArgs,
332 packageRoot: root, packageConfig: conf, paused: true);
333 // Wait for isolate to exit before exiting the main isolate.
334 var done = new RawReceivePort();
335 done.handler = (_) { done.close(); };
336 isolate.addExitHandler(done.sendPort);
337 isolate.resume(isolate.pauseCapability);
338 }
339 """;
340
341 /// Script that spawns a new Isolate using Isolate.spawn.
342 const String spawnMain = r"""
floitsch 2016/05/26 19:09:56 unused.
Lasse Reichstein Nielsen 2016/05/27 10:53:56 Ditto.
343 import "dart:isolate";
344 import "testmain.dart" as test;
345 main() async {
346 var isolate = await Isolate.spawn(test.main, [], paused: true);
347 // Wait for isolate to exit before exiting the main isolate.
348 var done = new RawReceivePort();
349 done.handler = (_) { done.close(); };
350 isolate.addExitHandler(done.sendPort);
351 isolate.resume(isolate.pauseCapability);
352 }
353 """;
354
355 /// A package directory containing only one package, "foo", with one file.
356 const Map fooPackage = const { "foo": const { "foo": "var x = 'qux';" }};
357
358 /// Runs the Dart executable with the provided parameters.
359 ///
360 /// Captures and returns the output.
361 Future<String> runDart(String script,
362 {String root, String config,
363 Iterable<String> scriptArgs}) async {
364 // TODO: Find a way to change CWD before running script.
365 var executable = Platform.executable;
366 var args = [];
367 if (root != null) args..add("-p")..add(root);
368 if (config != null) args..add("--packages=$config");
369 args.add(script);
370 if (scriptArgs != null) {
371 args.addAll(scriptArgs);
372 }
373 return Process.run(executable, args).then((results) {
374 if (results.exitCode != 0) {
375 throw results.stderr;
376 }
377 return results.stdout;
378 });
379 }
380
381 /// Creates a number of files and subdirectories.
382 ///
383 /// The [content] is the content of the directory itself. The map keys are
384 /// names and the values are either strings that represent Dart file contents
385 /// or maps that represent subdirectories.
386 /// Subdirectories may include a package directory. If [packageDir]
387 /// is provided, a `.packages` file is created for the content of that
388 /// directory.
389 void createFiles(Directory tempDir, Map content, String fixPaths(String text),
390 [String packageDir]) {
391 Directory createDir(Directory base, String name) {
392 Directory newDir = new Directory(p.join(base.path, name));
393 newDir.createSync();
394 return newDir;
395 }
396
397 void createTextFile(Directory base, String name, String content) {
398 File newFile = new File(p.join(base.path, name));
399 newFile.writeAsStringSync(fixPaths(content));
400 }
401
402 void createRecursive(Directory dir, Map map) {
403 for (var name in map.keys) {
404 var content = map[name];
405 if (content is String) {
406 // If the name starts with "." it's a .packages file, otherwise it's
407 // a dart file. Those are the only files we care about in this test.
408 createTextFile(dir,
409 name.startsWith(".") ? name : name + ".dart",
410 content);
411 } else {
412 assert(content is Map);
413 var subdir = createDir(dir, name);
414 createRecursive(subdir, content);
415 }
416 }
417 }
418
419 createRecursive(tempDir, content);
420 if (packageDir != null) {
421 // Unused?
422 Map packages = content[packageDir];
423 var entries =
424 packages.keys.map((key) => "$key:$packageDir/$key").join("\n");
425 createTextFile(tempDir, ".packages", entries);
426 }
427 return tempDir;
428 }
429
430 /// Start an HTTP server which serves a directory/file structure.
431 ///
432 /// The directories and files are described by [files].
433 ///
434 /// Each map key is an entry in a directory. A `Map` value is a sub-directory
435 /// and a `String` value is a text file.
436 /// The file contents are run through [fixPaths] to allow them to be self-
437 /// referential.
438 Future<HttpServer> startServer(Map files, String fixPaths(String text)) async {
439 return (await HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, 0))
440 ..forEach((request) {
441 var result = files;
442 onFailure: {
443 for (var part in request.uri.pathSegments) {
444 if (part.endsWith(".dart")) {
445 part = part.substring(0, part.length - 5);
446 }
447 if (result is Map) {
448 result = result[part];
449 } else {
450 break onFailure;
451 }
452 }
453 if (result is String) {
454 request.response..write(fixPaths(result))
455 ..close();
456 return;
457 }
458 }
459 request.response..statusCode = HttpStatus.NOT_FOUND
460 ..close();
461 });
462 }
463
464 // Counter used to avoid reusing temporary directory names.
465 // Some platforms are timer based, and creating two temp-dirs withing a short
466 // duration may cause a collision.
467 int tmpDirCounter = 0;
468
469 Directory createTempDir() {
470 return Directory.systemTemp.createTempSync("pftest-${tmpDirCounter++}-");
471 }
OLDNEW
« 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