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

Side by Side Diff: tests/standalone/packages_file_test.dart

Issue 2038033002: Revert "Add package-config tests where test is running in Isolate.spawn-isolates." (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Fix the bug. Created 4 years, 6 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 | « runtime/bin/builtin.dart ('k') | tests/standalone/standalone.status » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 import "dart:async"; 5 import "dart:async";
6 import "dart:io"; 6 import "dart:io";
7 import "dart:convert" show JSON; 7 import "dart:convert" show JSON;
8 import "package:path/path.dart" as p; 8 import "package:path/path.dart" as p;
9 import "package:async_helper/async_helper.dart"; 9 import "package:async_helper/async_helper.dart";
10 10
11 /// Root directory of generated files.
12 /// Path contains trailing slash.
13 /// Each configuration gets its own sub-directory.
14 Directory fileRoot;
15 /// Shared HTTP server serving the files in [httpFiles].
16 /// Each configuration gets its own "sub-dir" entry in `httpFiles`.
17 HttpServer httpServer;
18 /// Directory structure served by HTTP server.
19 Map<String, dynamic> httpFiles = {};
20 /// List of configurations.
21 List<Configuration> configurations = [];
22 /// Collection of failing tests and their failure messages.
23 ///
24 /// Each test may fail in more than one way.
25 var failingTests = <String, List<String>>{};
26
11 main() async { 27 main() async {
12 asyncStart(); 28 asyncStart();
13 29 await setUp();
14 // The `test` function can generate file or http resources. 30
15 // It replaces "%file/" with URI of the root directory of generated files and 31 await runTests(); /// 01: ok
16 // "%http/" with the URI of the HTTP server's root in appropriate contexts 32 await runTests([spawn]); /// 02: ok
17 // (all file contents and parameters). 33 await runTests([spawn, spawn]); /// 03: ok
18 34 await runTests([spawnUriInherit]); /// 04: ok
19 // With no specified resolutiuon and no implicit .packages or packages/ 35 await runTests([spawnUriInherit, spawn]); /// 05: ok
20 // available, nothing can be resolved and the package can't be imported. 36 await runTests([spawn, spawnUriInherit]); /// 06: ok
21 await test("file: no resolution", 37
22 "%file/main.dart", 38 // Test that spawning a new VM with file paths instead of URIs as arguments
23 file: {"main": testMain}, 39 // gives the same URIs in the internal values.
24 expect: {"foo.x": null}); 40 await runTests([asPath]); /// 07: ok
25 41
26 // An HTTP script with no ".packages" file assumes a "packages" dir. 42 // Test that spawnUri can reproduce the behavior of VM command line parameters
27 // All packages are resolved relative to that dir, whether it exists or not. 43 // exactly.
28 await test("http: no resolution", "%http/main.dart", 44 // (Don't run all configuration combinations in the same test, so
29 http: {"main": testMain}, 45 // unroll the configurations into multiple groups and run each group
46 // as its own multitest.
47 {
48 var groupCount = 8;
49 var groups = new List.generate(8, (_)=>[]);
50 for (int i = 0; i < configurations.length; i++) {
51 groups[i % groupCount].add(configurations[i]);
52 }
53 var group = -1;
54 group = 0; /// 10: ok
55 group = 1; /// 11: ok
56 group = 2; /// 12: ok
57 group = 3; /// 13: ok
58 group = 4; /// 14: ok
59 group = 5; /// 15: ok
60 group = 6; /// 16: ok
61 group = 7; /// 17: ok
62 if (group >= 0) {
63 for (var other in groups[group]) {
64 await runTests([spawnUriOther(other)]);
65 }
66 }
67 }
68
69
70 await tearDown();
71
72 if (failingTests.isNotEmpty) {
73 print("Errors found in tests:");
74 failingTests.forEach((test, actual) {
75 print("$test:\n ${actual.join("\n ")}");
76 });
77 exit(255);
78 }
79
80 asyncEnd();
81 }
82
83 /// Test running the test of the configuration through [Isolate.spawn].
84 ///
85 /// This should not change the expected results compared to running it
86 /// directly.
87 Configuration spawn(Configuration conf) {
88 // TODO(26555): Clean up when fixed.
89 // TEMPORARY FIX FOR ISSUE #26555 (http://dartbug.com/26555)
90 if (conf.expect["iroot"] == null &&
91 conf.expect["iconf"] == null &&
92 conf.expect["pconf"] != null) {
93 // The spawned isolate will do a search for a package file or root,
94 // which is not what the original did. Skip test for now.
95 return null;
96 }
97 // REMOVE WHEN ISSUE FIXED!
98 return conf.update(
99 description: conf.description + "/spawn",
100 main: "spawnMain",
101 newArgs: [conf.mainType],
102 // TEMPORARY FIX FOR ISSUE #26555 (http://dartbug.com/26555)
30 expect: { 103 expect: {
31 "iroot": "%http/packages/", 104 "proot": conf.expect["iroot"],
32 // "foo": null, 105 "pconf": conf.expect["iconf"],
33 "foo/": "%http/packages/foo/", 106 }
34 "foo/bar": "%http/packages/foo/bar", 107 // REMOVE WHEN ISSUE FIXED!
35 "foo.x": null, 108 );
36 }); 109 }
37 110
38 // A number of tests which behave similarly whether run as local files or 111 /// Tests running a spawnUri on top of the configuration before testing.
39 // over HTTP. 112 ///
40 for (var scheme in ["file", "http"]) { 113 /// The `spawnUri` call has no explicit root or config parameter, and
41 114 /// shouldn't search for one, so it implicitly inherits the current isolate's
42 /// Run a test in the current scheme. 115 /// actual root or configuration.
43 /// 116 Configuration spawnUriInherit(Configuration conf) {
44 /// The files are served either through HTTP or in a local directory. 117 if (conf.expect["iroot"] == null &&
45 /// Use "%$scheme/" to refer to the root of the served files. 118 conf.expect["iconf"] == null &&
46 testScheme(name, main, {expect, files, args, root, config}) { 119 conf.expect["pconf"] != null) {
47 return test("$scheme: $name", main, expect: expect, 120 // This means that the specified configuration file didn't exist.
48 root: root, config: config, args: args, 121 // spawning a new URI to "inherit" that will actually do an automatic
49 file: scheme == "file" ? files : null, 122 // package resolution search with results that are unpredictable.
50 http: scheme == "http" ? files : null); 123 // That behavior will be tested in a setting where we have more control over
51 } 124 // the files around the spawned URI.
52 125 return null;
53 { 126 }
54 var files = {"main": testMain, "packages": fooPackage}; 127 return conf.update(
55 // Expect implicitly detected package dir. 128 description: conf.description + "/spawnUri-inherit",
56 await testScheme("implicit packages dir","%$scheme/main.dart", 129 main: "spawnUriMain",
57 files: files, 130 // encode null parameters as "-". Windows fails if using empty string.
58 expect: { 131 newArgs: [conf.mainFile, "-", "-", "false"],
59 "iroot": "%$scheme/packages/", 132 expect: {
60 // "foo": null, 133 "proot": conf.expect["iroot"],
61 "foo/": "%$scheme/packages/foo/", 134 "pconf": conf.expect["iconf"],
62 "foo/bar": "%$scheme/packages/foo/bar", 135 }
63 }); 136 );
64 } 137 }
65 138
66 { 139 /// Tests running a spawnUri with an explicit configuration different
67 var files = {"sub": {"main": testMain, "packages": fooPackage}, 140 /// from the original configuration.
68 ".packages": ""}; 141 ///
69 // Expect implicitly detected package dir. 142 /// Duplicates the explicit parameters as arguments to the spawned isolate.
70 // Should not detect the .packages file in parent directory. 143 ConfigurationTransformer spawnUriOther(Configuration other) {
71 // That file is empty, so if it is used, the system cannot resolve "foo". 144 return (Configuration conf) {
72 await testScheme("implicit packages dir 2", "%$scheme/sub/main.dart", 145 bool search = (other.config == null) && (other.root == null);
73 files: files, 146 return conf.update(
74 expect: { 147 description: "${conf.description} -spawnUri-> ${other.description}",
75 "iroot": "%$scheme/sub/packages/", 148 main: "spawnUriMain",
76 // "foo": null, 149 newArgs: [other.mainFile,
77 "foo/": "%$scheme/sub/packages/foo/", 150 other.config ?? "-", other.root ?? "-", "$search"],
78 "foo/bar": "%$scheme/sub/packages/foo/bar", 151 expect: other.expect
79 }); 152 );
80 } 153 };
81 154 }
82 { 155
83 var files = {"main": testMain, 156
84 ".packages": "foo:pkgs/foo/", 157 /// Convert command line parameters to file paths.
85 "pkgs": fooPackage}; 158 ///
86 await testScheme("implicit .packages file", "%$scheme/main.dart", 159 /// This only works on the command line, not with `spawnUri`.
87 files: files, 160 Configuration asPath(Configuration conf) {
88 expect: { 161 bool change = false;
89 "iconf": "%$scheme/.packages", 162
90 // "foo": null, 163 String toPath(String string) {
91 "foo/": "%$scheme/pkgs/foo/", 164 if (string == null) return null;
92 "foo/bar": "%$scheme/pkgs/foo/bar", 165 if (string.startsWith("file:")) {
93 }); 166 change = true;
94 } 167 return new File.fromUri(Uri.parse(string)).path;
95 168 }
96 { 169 return string;
97 var files = {"main": testMain, 170 }
98 ".packages": "foo:packages/foo/", 171
99 "packages": fooPackage, 172 var mainFile = toPath(conf.mainFile);
100 "pkgs": fooPackage}; 173 var root = toPath(conf.root);
101 await testScheme("explicit package root, no slash", "%$scheme/main.dart", 174 var config = toPath(conf.config);
102 files: files, 175 if (!change) return null;
103 root: "%$scheme/pkgs", 176 return conf.update(description: conf.description + "/as path",
104 expect: { 177 mainFile: mainFile, root: root, config: config);
105 "proot": "%$scheme/pkgs/", 178 }
106 "iroot": "%$scheme/pkgs/", 179
107 // "foo": null, 180 /// --------------------------------------------------------------
108 "foo/": "%$scheme/pkgs/foo/", 181
109 "foo/bar": "%$scheme/pkgs/foo/bar", 182
110 }); 183 Future setUp() async {
111 } 184 fileRoot = createTempDir();
112 185 // print("FILES: $fileRoot");
113 { 186 httpServer = await startServer(httpFiles);
114 var files = {"main": testMain, 187 // print("HTTPS: ${httpServer.address.address}:${httpServer.port}");
115 ".packages": "foo:packages/foo/", 188 createConfigurations();
116 "packages": fooPackage, 189 }
117 "pkgs": fooPackage}; 190
118 await testScheme("explicit package root, slash", "%$scheme/main.dart", 191 Future tearDown() async {
119 files: files, 192 fileRoot.deleteSync(recursive: true);
120 root: "%$scheme/pkgs", 193 await httpServer.close();
121 expect: { 194 }
122 "proot": "%$scheme/pkgs/", 195
123 "iroot": "%$scheme/pkgs/", 196 typedef Configuration ConfigurationTransformer(Configuration conf);
124 // "foo": null, 197
125 "foo/": "%$scheme/pkgs/foo/", 198 Future runTests([List<ConfigurationTransformer> transformations]) async {
126 "foo/bar": "%$scheme/pkgs/foo/bar", 199 outer: for (var config in configurations) {
127 }); 200 if (transformations != null) {
128 } 201 for (int i = transformations.length - 1; i >= 0; i--) {
129 202 config = transformations[i](config);
130 { 203 if (config == null) {
131 var files = {"main": testMain, 204 continue outer; // Can be used to skip some tests.
132 ".packages": "foo:packages/foo/", 205 }
133 "packages": fooPackage, 206 }
134 ".pkgs": "foo:pkgs/foo/", 207 }
135 "pkgs": fooPackage}; 208 await testConfiguration(config);
136 await testScheme("explicit package config file", "%$scheme/main.dart", 209 }
137 files: files, 210 }
138 config: "%$scheme/.pkgs", 211
139 expect: { 212 // Creates a combination of configurations for running the Dart VM.
140 "pconf": "%$scheme/.pkgs", 213 //
141 "iconf": "%$scheme/.pkgs", 214 // The combinations covers most configurations of implicit and explicit
142 // "foo": null, 215 // package configurations over both file: and http: file sources.
143 "foo/": "%$scheme/pkgs/foo/", 216 // It also specifies the expected values of the following for a VM
144 "foo/bar": "%$scheme/pkgs/foo/bar", 217 // run in that configuration.
145 }); 218 //
146 } 219 // * `Process.packageRoot`
147 220 // * `Process.packageConfig`
148 { 221 // * `Isolate.packageRoot`
149 /// The package config can be specified as a data: URI. 222 // * `Isolate.packageRoot`
150 /// (In that case, relative URI references in the config file won't work). 223 // * `Isolate.resolvePacakgeUri` of various inputs.
151 var files = {"main": testMain, 224 // * A variable defined in a library loaded using a `package:` URI.
152 ".packages": "foo:packages/foo/", 225 //
153 "packages": fooPackage, 226 // The configurations all have URIs as `root`, `config` and `mainFile` strings,
154 "pkgs": fooPackage}; 227 // have empty argument lists and `mainFile` points to the the `main.dart` file.
155 var dataUri = "data:,foo:%$scheme/pkgs/foo/\n"; 228 void createConfigurations() {
156 await testScheme("explicit data: config file", "%$scheme/main.dart", 229 add(String description, String mainDir, {String root, String config,
157 files: files, 230 Map file, Map http, Map expect}) {
158 config: dataUri, 231 var id = freshName("conf");
159 expect: { 232
160 "pconf": dataUri, 233 file ??= {};
161 "iconf": dataUri, 234 http ??= {};
162 // "foo": null, 235
163 "foo/": "%$scheme/pkgs/foo/", 236 // Fix-up paths.
164 "foo/bar": "%$scheme/pkgs/foo/bar", 237 String fileUri = fileRoot.uri.resolve("$id/").toString();
165 }); 238 String httpUri =
166 } 239 "http://${httpServer.address.address}:${httpServer.port}/$id/";
167 } 240
168 241 String fixPath(String path) {
169 { 242 return path?.replaceAllMapped(fileHttpRegexp, (match) {
170 // With a file: URI, the lookup checks for a .packages file in superdirs 243 if (path.startsWith("%file/", match.start)) return fileUri;
171 // when it fails to find a ,packages file or packages/ directory next to 244 return httpUri;
172 // the entry point.
173 var files = {"sub": { "main": testMain },
174 ".packages": "foo:pkgs/foo/",
175 "pkgs": fooPackage};
176 await test("file: implicit .packages file in ..", "%file/sub/main.dart",
177 file: files,
178 expect: {
179 "iconf": "%file/.packages",
180 // "foo": null,
181 "foo/": "%file/pkgs/foo/",
182 "foo/bar": "%file/pkgs/foo/bar",
183 }); 245 });
184 } 246 }
185 247
186 { 248 void fixPaths(Map dirs) {
187 // With a non-file: URI, the lookup assumes a packges/ dir. 249 for (var name in dirs.keys) {
188 // The absence of a .packages file next to the entry point means 250 var value = dirs[name];
189 // that the resolution assumes a packages directory, whether it exists or 251 if (value is Map) {
190 // not. It should not find the .packages file in the parent directory. 252 Map subDir = value;
191 var files = {"sub": { "main": testMain }, 253 fixPaths(subDir);
192 ".packages": "foo:pkgs/foo/", 254 } else {
193 "pkgs": fooPackage}; 255 var newValue = fixPath(value);
194 await test("http: implicit packages dir", "%http/sub/main.dart", 256 if (newValue != value) dirs[name] = newValue;
195 http: files, 257 }
196 expect: { 258 }
197 "iroot": "%http/sub/packages/", 259 }
198 // "foo": null, 260
199 "foo/": "%http/sub/packages/foo/", 261 if (!mainDir.endsWith("/")) mainDir += "/";
200 "foo/bar": "%http/sub/packages/foo/bar", 262 // Insert main files into the main-dir map.
201 "foo.x": null, 263 Map mainDirMap;
202 }); 264 {
203 } 265 if (mainDir.startsWith("%file/")) {
204 266 mainDirMap = file;
205 267 } else {
206 if (failingTests.isNotEmpty) { 268 mainDirMap = http;
207 print("Errors found in tests:\n ${failingTests.join("\n ")}\n"); 269
208 exit(255); 270 }
209 } 271 var parts = mainDir.split('/');
210 asyncEnd(); 272 for (int i = 1; i < parts.length - 1; i++) {
211 } 273 var dirName = parts[i];
212 274 mainDirMap = mainDirMap[dirName] ?? (mainDirMap[dirName] = {});
213 // --------------------------------------------------------- 275 }
214 // Helper functionality. 276 }
215 277
216 var failingTests = new Set(); 278 mainDirMap["main"] = testMain;
217 279 mainDirMap["spawnMain"] = spawnMain.replaceAll("%mainDir/", mainDir);
218 var fileHttpRegexp = new RegExp(r"%(?:file|http)/"); 280 mainDirMap["spawnUriMain"] = spawnUriMain;
219 281
220 Future test(String name, String main, 282 mainDir = fixPath(mainDir);
221 {String root, String config, List<String> args, 283 root = fixPath(root);
222 Map file, Map http, Map expect}) async { 284 config = fixPath(config);
223 // Default values that are easily recognized in output. 285 fixPaths(file);
224 String fileRoot = "<no files configured>"; 286 fixPaths(http);
225 String httpRoot = "<no http server configured>";
226
227 /// Replaces markers `%file/` and `%http/` with the actual locations.
228 ///
229 /// Accepts a `null` [source] and returns `null` again.
230 String fixPaths(String source) {
231 if (source == null) return null;
232 var result = source.replaceAllMapped(fileHttpRegexp, (match) {
233 if (source.startsWith("file", match.start + 1)) return fileRoot;
234 return httpRoot;
235 });
236 return result;
237 }
238
239 // Set up temporary directory or HTTP server.
240 Directory tmpDir;
241 var https;
242 if (file != null) {
243 tmpDir = createTempDir();
244 fileRoot = new Uri.directory(tmpDir.path).toString();
245 }
246 if (http != null) {
247 https = await startServer(http, fixPaths);
248 httpRoot = "http://${https.address.address}:${https.port}/";
249 }
250 if (file != null) {
251 // Create files after both roots are known, to allow file content
252 // to refer to the them.
253 createFiles(tmpDir, file, fixPaths);
254 }
255
256 try {
257 var output = await runDart(fixPaths(main),
258 root: fixPaths(root),
259 config: fixPaths(config),
260 scriptArgs: args?.map(fixPaths));
261 // These expectations are default. If not overridden the value will be 287 // These expectations are default. If not overridden the value will be
262 // expected to be null. That is, you can't avoid testing the actual 288 // expected to be null. That is, you can't avoid testing the actual
263 // value of these, you can only change what value to expect. 289 // value of these, you can only change what value to expect.
264 // For values not included here (commented out), the result is not tested 290 // For values not included here (commented out), the result is not tested
265 // unless a value (maybe null) is provided. 291 // unless a value (maybe null) is provided.
266 var expects = { 292 fixPaths(expect);
267 "pconf": null, 293
268 "proot": null, 294 expect = {
269 "iconf": null, 295 "pconf": null,
270 "iroot": null, 296 "proot": null,
271 // "foo": null, 297 "iconf": null,
272 "foo/": null, 298 "iroot": null,
273 "foo/bar": null, 299 // "foo": null,
274 "foo.x": "qux", 300 "foo/": null,
275 }..addAll(expect); 301 "foo/bar": null,
276 match(JSON.decode(output), expects, fixPaths, name); 302 "foo.x": "qux",
303 }..addAll(expect ?? const {});
304
305 // Add http files to the http server.
306 if (http.isNotEmpty) {
307 httpFiles[id] = http;
308 }
309 // Add file files to the file system.
310 if (file.isNotEmpty) {
311 createFiles(fileRoot, id, file);
312 }
313
314 configurations.add(new Configuration(
315 description: description,
316 root: root,
317 config: config,
318 mainFile: mainDir + "main.dart",
319 args: const [],
320 expect: expect));
321 }
322
323 // The `test` function can generate file or http resources.
324 // It replaces "%file/" with URI of the root directory of generated files and
325 // "%http/" with the URI of the HTTP server's root in appropriate contexts
326 // (all file contents and parameters).
327
328 // Tests that only use one scheme to access files.
329 for (var scheme in ["file", "http"]) {
330
331 /// Run a test in the current scheme.
332 ///
333 /// The files are served either through HTTP or in a local directory.
334 /// Use "%$scheme/" to refer to the root of the served files.
335 addScheme(description, main, {expect, files, args, root, config}) {
336 add("$scheme/$description", main, expect: expect,
337 root: root, config: config,
338 file: (scheme == "file") ? files : null,
339 http: (scheme == "http") ? files : null);
340 }
341
342 {
343 // No parameters, no .packages files or packages/ dir.
344 // A "file:" source realizes there is no configuration and can't resolve
345 // any packages, but a "http:" source assumes a "packages/" directory.
346 addScheme("no resolution",
347 "%$scheme/",
348 files: {},
349 expect: (scheme == "file") ? {
350 "foo.x": null
351 } : {
352 "iroot": "%http/packages/",
353 "foo/": "%http/packages/foo/",
354 "foo/bar": "%http/packages/foo/bar",
355 "foo.x": null,
356 });
357 }
358
359 {
360 // No parameters, no .packages files,
361 // packages/ dir exists and is detected.
362 var files = {"packages": fooPackage};
363 addScheme("implicit packages dir","%$scheme/",
364 files: files,
365 expect: {
366 "iroot": "%$scheme/packages/",
367 "foo/": "%$scheme/packages/foo/",
368 "foo/bar": "%$scheme/packages/foo/bar",
369 });
370 }
371
372 {
373 // No parameters, no .packages files in current dir, but one in parent,
374 // packages/ dir exists and is used.
375 //
376 // Should not detect the .packages file in parent directory.
377 // That file is empty, so if it is used, the system cannot resolve "foo".
378 var files = {"sub": {"packages": fooPackage},
379 ".packages": ""};
380 addScheme("implicit packages dir overrides parent .packages",
381 "%$scheme/sub/",
382 files: files,
383 expect: {
384 "iroot": "%$scheme/sub/packages/",
385 "foo/": "%$scheme/sub/packages/foo/",
386 "foo/bar": "%$scheme/sub/packages/foo/bar",
387 // "foo.x": "qux", // Blocked by issue http://dartbug.com/26482
388 });
389 }
390
391 {
392 // No parameters, a .packages file next to entry is found and used.
393 // A packages/ directory is ignored.
394 var files = {".packages": "foo:pkgs/foo/",
395 "packages": {},
396 "pkgs": fooPackage};
397 addScheme("implicit .packages file", "%$scheme/",
398 files: files,
399 expect: {
400 "iconf": "%$scheme/.packages",
401 "foo/": "%$scheme/pkgs/foo/",
402 "foo/bar": "%$scheme/pkgs/foo/bar",
403 });
404 }
405
406 {
407 // No parameters, a .packages file in parent dir, no packages/ dir.
408 // With a file: URI, find the .packages file.
409 // WIth a http: URI, assume a packages/ dir.
410 var files = {"sub": {},
411 ".packages": "foo:pkgs/foo/",
412 "pkgs": fooPackage};
413 addScheme(".packages file in parent", "%$scheme/sub/",
414 files: files,
415 expect: (scheme == "file") ? {
416 "iconf": "%file/.packages",
417 "foo/": "%file/pkgs/foo/",
418 "foo/bar": "%file/pkgs/foo/bar",
419 } : {
420 "iroot": "%http/sub/packages/",
421 "foo/": "%http/sub/packages/foo/",
422 "foo/bar": "%http/sub/packages/foo/bar",
423 "foo.x": null,
424 });
425 }
426
427 {
428 // Specified package root that doesn't exist.
429 // Ignores existing .packages file and packages/ dir.
430 addScheme("explicit root not there",
431 "%$scheme/",
432 files: {"packages": fooPackage,
433 ".packages": "foo:%$scheme/packages/"},
434 root: "%$scheme/notthere/",
435 expect: {
436 "proot": "%$scheme/notthere/",
437 "iroot": "%$scheme/notthere/",
438 "foo/": "%$scheme/notthere/foo/",
439 "foo/bar": "%$scheme/notthere/foo/bar",
440 "foo.x": null,
441 });
442 }
443
444 {
445 // Specified package config that doesn't exist.
446 // Ignores existing .packages file and packages/ dir.
447 addScheme("explicit config not there",
448 "%$scheme/",
449 files: {".packages": "foo:packages/foo/",
450 "packages": fooPackage},
451 config: "%$scheme/.notthere",
452 expect: {
453 "pconf": "%$scheme/.notthere",
454 "iconf": null, // <- Only there if actually loaded (unspecified).
455 "foo/": null,
456 "foo/bar": null,
457 "foo.x": null,
458 });
459 }
460
461 {
462 // Specified package root with no trailing slash.
463 // The Platform.packageRoot and Isolate.packageRoot has a trailing slash.
464 var files = {".packages": "foo:packages/foo/",
465 "packages": {},
466 "pkgs": fooPackage};
467 addScheme("explicit package root, no slash", "%$scheme/",
468 files: files,
469 root: "%$scheme/pkgs",
470 expect: {
471 "proot": "%$scheme/pkgs/",
472 "iroot": "%$scheme/pkgs/",
473 "foo/": "%$scheme/pkgs/foo/",
474 "foo/bar": "%$scheme/pkgs/foo/bar",
475 });
476 }
477
478 {
479 // Specified package root with trailing slash.
480 var files = {".packages": "foo:packages/foo/",
481 "packages": {},
482 "pkgs": fooPackage};
483 addScheme("explicit package root, slash", "%$scheme/",
484 files: files,
485 root: "%$scheme/pkgs",
486 expect: {
487 "proot": "%$scheme/pkgs/",
488 "iroot": "%$scheme/pkgs/",
489 "foo/": "%$scheme/pkgs/foo/",
490 "foo/bar": "%$scheme/pkgs/foo/bar",
491 });
492 }
493
494 {
495 // Specified package config.
496 var files = {".packages": "foo:packages/foo/",
497 "packages": {},
498 ".pkgs": "foo:pkgs/foo/",
499 "pkgs": fooPackage};
500 addScheme("explicit package config file", "%$scheme/",
501 files: files,
502 config: "%$scheme/.pkgs",
503 expect: {
504 "pconf": "%$scheme/.pkgs",
505 "iconf": "%$scheme/.pkgs",
506 "foo/": "%$scheme/pkgs/foo/",
507 "foo/bar": "%$scheme/pkgs/foo/bar",
508 });
509 }
510
511 {
512 // Specified package config as data: URI.
513 // The package config can be specified as a data: URI.
514 // (In that case, relative URI references in the config file won't work).
515 var files = {".packages": "foo:packages/foo/",
516 "packages": {},
517 "pkgs": fooPackage};
518 var dataUri = "data:,foo:%$scheme/pkgs/foo/\n";
519 addScheme("explicit data: config file", "%$scheme/",
520 files: files,
521 config: dataUri,
522 expect: {
523 "pconf": dataUri,
524 "iconf": dataUri,
525 "foo/": "%$scheme/pkgs/foo/",
526 "foo/bar": "%$scheme/pkgs/foo/bar",
527 });
528 }
529 }
530
531 // Tests where there are files on both http: and file: sources.
532
533 for (var entryScheme in const ["file", "http"]) {
534 for (var pkgScheme in const ["file", "http"]) {
535 // Package root.
536 if (entryScheme != pkgScheme) {
537 // Package dir and entry point on different schemes.
538 var files = {};
539 var https = {};
540 (entryScheme == "file" ? files : https)["main"] = testMain;
541 (pkgScheme == "file" ? files : https)["pkgs"] = fooPackage;
542 add("$pkgScheme pkg/$entryScheme main", "%$entryScheme/",
543 file: files, http: https,
544 root: "%$pkgScheme/pkgs/",
545 expect: {
546 "proot": "%$pkgScheme/pkgs/",
547 "iroot": "%$pkgScheme/pkgs/",
548 "foo/": "%$pkgScheme/pkgs/foo/",
549 "foo/bar": "%$pkgScheme/pkgs/foo/bar",
550 "foo.x": "qux",
551 });
552 }
553 // Package config. The configuration file may also be on either source.
554 for (var configScheme in const ["file", "http"]) {
555 // Don't do the boring stuff!
556 if (entryScheme == configScheme && entryScheme == pkgScheme) continue;
557 // Package config, packages and entry point not all on same scheme.
558 var files = {};
559 var https = {};
560 (entryScheme == "file" ? files : https)["main"] = testMain;
561 (configScheme == "file" ? files : https)[".pkgs"] =
562 "foo:%$pkgScheme/pkgs/foo/\n";
563 (pkgScheme == "file" ? files : https)["pkgs"] = fooPackage;
564 add("$pkgScheme pkg/$configScheme config/$entryScheme main",
565 "%$entryScheme/",
566 file: files, http: https,
567 config: "%$configScheme/.pkgs",
568 expect: {
569 "pconf": "%$configScheme/.pkgs",
570 "iconf": "%$configScheme/.pkgs",
571 "foo/": "%$pkgScheme/pkgs/foo/",
572 "foo/bar": "%$pkgScheme/pkgs/foo/bar",
573 "foo.x": "qux",
574 });
575 }
576 }
577 }
578 }
579
580
581 // ---------------------------------------------------------
582 // Helper functionality.
583
584 var fileHttpRegexp = new RegExp(r"%(?:file|http)/");
585
586 // Executes a test in a configuration.
587 //
588 // The test must specify which main file to use
589 // (`main`, `spawnMain` or `spawnUriMain`)
590 // and any arguments which will be used by `spawnMain` and `spawnUriMain`.
591 //
592 // The [expect] map may be used to override the expectations of the
593 // configuration on a value-by-value basis. Passing, e.g., `{"pconf": null}`
594 // will override only the `pconf` (`Platform.packageConfig`) expectation.
595 Future testConfiguration(Configuration conf) async {
596 print("-- ${conf.description}");
597 var description = conf.description;
598 try {
599 var output = await execDart(conf.mainFile,
600 root: conf.root,
601 config: conf.config,
602 scriptArgs: conf.args);
603 match(JSON.decode(output), conf.expect, description, output);
277 } catch (e, s) { 604 } catch (e, s) {
278 // Unexpected error calling runDart or parsing the result. 605 // Unexpected error calling execDart or parsing the result.
279 // Report it and continue. 606 // Report it and continue.
280 print("ERROR running $name: $e\n$s"); 607 print("ERROR running $description: $e\n$s");
281 failingTests.add(name); 608 failingTests.putIfAbsent(description, () => []).add("$e");
282 } finally { 609 }
283 if (https != null) await https.close(); 610 }
284 if (tmpDir != null) tmpDir.deleteSync(recursive: true); 611
285 }
286 }
287
288 612
289 /// Test that the output of running testMain matches the expectations. 613 /// Test that the output of running testMain matches the expectations.
290 /// 614 ///
291 /// The output is a string which is parse as a JSON literal. 615 /// The output is a string which is parse as a JSON literal.
292 /// The resulting map is always mapping strings to strings, or possibly `null`. 616 /// The resulting map is always mapping strings to strings, or possibly `null`.
293 /// The expectations can have non-string values other than null, 617 /// The expectations can have non-string values other than null,
294 /// they are `toString`'ed before being compared (so the caller can use a URI 618 /// they are `toString`'ed before being compared (so the caller can use a URI
295 /// or a File/Directory directly as an expectation). 619 /// or a File/Directory directly as an expectation).
296 void match(Map actuals, Map expectations, String fixPaths(String expectation), 620 void match(Map actuals, Map expectations, String desc, String actualJson) {
297 String name) {
298 for (var key in expectations.keys) { 621 for (var key in expectations.keys) {
299 var expectation = fixPaths(expectations[key]?.toString()); 622 var expectation = expectations[key]?.toString();
300 var actual = actuals[key]; 623 var actual = actuals[key];
301 if (expectation != actual) { 624 if (expectation != actual) {
302 print("ERROR: $name: $key: Expected: <$expectation> Found: <$actual>"); 625 print("ERROR: $desc: $key: Expected: <$expectation> Found: <$actual>");
303 failingTests.add(name); 626 failingTests.putIfAbsent(desc, ()=>[]).add(
627 "$key: $expectation != $actual");
304 } 628 }
305 } 629 }
306 } 630 }
307 631
632 const String improt = "import"; // Avoid multitest import rewriting.
633
308 /// Script that prints the current state and the result of resolving 634 /// Script that prints the current state and the result of resolving
309 /// a few package URIs. This script will be invoked in different settings, 635 /// a few package URIs. This script will be invoked in different settings,
310 /// and the result will be parsed and compared to the expectations. 636 /// and the result will be parsed and compared to the expectations.
311 const String testMain = r""" 637 const String testMain = """
312 import "dart:convert" show JSON; 638 $improt "dart:convert" show JSON;
313 import "dart:io" show Platform, Directory; 639 $improt "dart:io" show Platform, Directory;
314 import "dart:isolate" show Isolate; 640 $improt "dart:isolate" show Isolate;
315 import "package:foo/foo.dart" deferred as foo; 641 $improt "package:foo/foo.dart" deferred as foo;
316 main(_) async { 642 main(_) async {
317 String platformRoot = await Platform.packageRoot; 643 String platformRoot = await Platform.packageRoot;
318 String platformConfig = await Platform.packageConfig; 644 String platformConfig = await Platform.packageConfig;
319 Directory cwd = Directory.current; 645 Directory cwd = Directory.current;
320 Uri script = Platform.script; 646 Uri script = Platform.script;
321 Uri isolateRoot = await Isolate.packageRoot; 647 Uri isolateRoot = await Isolate.packageRoot;
322 Uri isolateConfig = await Isolate.packageConfig; 648 Uri isolateConfig = await Isolate.packageConfig;
323 Uri base = Uri.base; 649 Uri base = Uri.base;
324 Uri res1 = await Isolate.resolvePackageUri(Uri.parse("package:foo")); 650 Uri res1 = await Isolate.resolvePackageUri(Uri.parse("package:foo"));
325 Uri res2 = await Isolate.resolvePackageUri(Uri.parse("package:foo/")); 651 Uri res2 = await Isolate.resolvePackageUri(Uri.parse("package:foo/"));
(...skipping 13 matching lines...) Expand all
339 "foo": res1?.toString(), 665 "foo": res1?.toString(),
340 "foo/": res2?.toString(), 666 "foo/": res2?.toString(),
341 "foo/bar": res3?.toString(), 667 "foo/bar": res3?.toString(),
342 "foo.x": fooX?.toString(), 668 "foo.x": fooX?.toString(),
343 })); 669 }));
344 } 670 }
345 """; 671 """;
346 672
347 /// Script that spawns a new Isolate using Isolate.spawnUri. 673 /// Script that spawns a new Isolate using Isolate.spawnUri.
348 /// 674 ///
349 /// Takes URI of target isolate, package config and package root as 675 /// Takes URI of target isolate, package config, package root and
350 /// command line arguments. Any further arguments are forwarded to the 676 /// automatic package resolution-flag parameters as command line arguments.
351 /// spawned isolate. 677 /// Any further arguments are forwarded to the spawned isolate.
352 const String spawnUriMain = r""" 678 const String spawnUriMain = """
353 import "dart:isolate"; 679 $improt "dart:isolate";
680 $improt "dart:async";
354 main(args) async { 681 main(args) async {
355 Uri target = Uri.parse(args[0]); 682 Uri target = Uri.parse(args[0]);
356 Uri conf = args.length > 1 && args[1].isNotEmpty ? Uri.parse(args[1]) : null; 683 Uri config = (args[1] == "-") ? null : Uri.parse(args[1]);
357 Uri root = args.length > 2 && args[2].isNotEmpty ? Uri.parse(args[2]) : null; 684 Uri root = (args[2] == "-") ? null : Uri.parse(args[2]);
358 var restArgs = args.skip(3).toList(); 685 bool search = args[3] == "true";
359 var isolate = await Isolate.spawnUri(target, restArgs, 686 var restArgs = args.skip(4).toList();
360 packageRoot: root, packageConfig: conf, paused: true); 687 // Port keeps isolate alive until spawned isolate terminates.
361 // Wait for isolate to exit before exiting the main isolate. 688 var port = new RawReceivePort();
362 var done = new RawReceivePort(); 689 port.handler = (res) async {
363 done.handler = (_) { done.close(); }; 690 port.close(); // Close on exit or first error.
364 isolate.addExitHandler(done.sendPort); 691 if (res != null) {
365 isolate.resume(isolate.pauseCapability); 692 await new Future.error(res[0], new StackTrace.fromString(res[1]));
693 }
694 };
695 Isolate.spawnUri(target, restArgs, null,
696 packageRoot: root, packageConfig: config,
697 automaticPackageResolution: search,
698 onError: port.sendPort, onExit: port.sendPort);
366 } 699 }
367 """; 700 """;
368 701
369 /// Script that spawns a new Isolate using Isolate.spawn. 702 /// Script that spawns a new Isolate using Isolate.spawn.
370 const String spawnMain = r""" 703 ///
371 import "dart:isolate"; 704 /// Uses the first argument to select which target to spawn.
372 import "testmain.dart" as test; 705 /// Should be either "test", "uri" or "spawn".
373 main() async { 706 const String spawnMain = """
374 var isolate = await Isolate.spawn(test.main, [], paused: true); 707 $improt "dart:async";
375 // Wait for isolate to exit before exiting the main isolate. 708 $improt "dart:isolate";
376 var done = new RawReceivePort(); 709 $improt "%mainDir/main.dart" as test;
377 done.handler = (_) { done.close(); }; 710 $improt "%mainDir/spawnUriMain.dart" as spawnUri;
378 isolate.addExitHandler(done.sendPort); 711 main(List<String> args) async {
379 isolate.resume(isolate.pauseCapability); 712 // Port keeps isolate alive until spawned isolate terminates.
713 var port = new RawReceivePort();
714 port.handler = (res) async {
715 port.close(); // Close on exit or first error.
716 if (res != null) {
717 await new Future.error(res[0], new StackTrace.fromString(res[1]));
718 }
719 };
720 var arg = args.first;
721 var rest = args.skip(1).toList();
722 var target;
723 if (arg == "main") {
724 target = test.main;
725 } else if (arg == "spawnUriMain") {
726 target = spawnUri.main;
727 } else {
728 target = main;
729 }
730 Isolate.spawn(target, rest, onError: port.sendPort, onExit: port.sendPort);
380 } 731 }
381 """; 732 """;
382 733
383 /// A package directory containing only one package, "foo", with one file. 734 /// A package directory containing only one package, "foo", with one file.
384 const Map fooPackage = const { "foo": const { "foo": "var x = 'qux';" }}; 735 const Map fooPackage = const { "foo": const { "foo": "var x = 'qux';" }};
385 736
737
386 /// Runs the Dart executable with the provided parameters. 738 /// Runs the Dart executable with the provided parameters.
387 /// 739 ///
388 /// Captures and returns the output. 740 /// Captures and returns the output.
389 Future<String> runDart(String script, 741 Future<String> execDart(String script,
390 {String root, String config, 742 {String root, String config,
391 Iterable<String> scriptArgs}) async { 743 Iterable<String> scriptArgs}) async {
744 var checked = false;
745 assert((checked = true));
392 // TODO: Find a way to change CWD before running script. 746 // TODO: Find a way to change CWD before running script.
393 var executable = Platform.executable; 747 var executable = Platform.executable;
394 var args = []; 748 var args = [];
395 if (root != null) args..add("-p")..add(root); 749 if (checked) args.add("--checked");
396 if (config != null) args..add("--packages=$config"); 750 if (root != null) args.add("--package-root=$root");
751 if (config != null) args.add("--packages=$config");
397 args.add(script); 752 args.add(script);
398 if (scriptArgs != null) { 753 if (scriptArgs != null) {
399 args.addAll(scriptArgs); 754 args.addAll(scriptArgs);
400 } 755 }
401 return Process.run(executable, args).then((results) { 756 return Process.run(executable, args).then((results) {
402 if (results.exitCode != 0) { 757 if (results.exitCode != 0 || results.stderr.isNotEmpty) {
403 throw results.stderr; 758 throw results.stderr;
404 } 759 }
405 return results.stdout; 760 return results.stdout;
406 }); 761 });
407 } 762 }
408 763
409 /// Creates a number of files and subdirectories. 764 /// Creates a number of files and subdirectories.
410 /// 765 ///
411 /// The [content] is the content of the directory itself. The map keys are 766 /// The [content] is the content of the directory itself. The map keys are
412 /// names and the values are either strings that represent Dart file contents 767 /// names and the values are either strings that represent Dart file contents
413 /// or maps that represent subdirectories. 768 /// or maps that represent subdirectories.
414 /// Subdirectories may include a package directory. If [packageDir] 769 void createFiles(Directory tempDir, String subDir, Map content) {
415 /// is provided, a `.packages` file is created for the content of that
416 /// directory.
417 void createFiles(Directory tempDir, Map content, String fixPaths(String text),
418 [String packageDir]) {
419 Directory createDir(Directory base, String name) { 770 Directory createDir(Directory base, String name) {
420 Directory newDir = new Directory(p.join(base.path, name)); 771 Directory newDir = new Directory(p.join(base.path, name));
421 newDir.createSync(); 772 newDir.createSync();
422 return newDir; 773 return newDir;
423 } 774 }
424 775
425 void createTextFile(Directory base, String name, String content) { 776 void createTextFile(Directory base, String name, String content) {
426 File newFile = new File(p.join(base.path, name)); 777 File newFile = new File(p.join(base.path, name));
427 newFile.writeAsStringSync(fixPaths(content)); 778 newFile.writeAsStringSync(content);
428 } 779 }
429 780
430 void createRecursive(Directory dir, Map map) { 781 void createRecursive(Directory dir, Map map) {
431 for (var name in map.keys) { 782 for (var name in map.keys) {
432 var content = map[name]; 783 var content = map[name];
433 if (content is String) { 784 if (content is String) {
434 // If the name starts with "." it's a .packages file, otherwise it's 785 // If the name starts with "." it's a .packages file, otherwise it's
435 // a dart file. Those are the only files we care about in this test. 786 // a dart file. Those are the only files we care about in this test.
436 createTextFile(dir, 787 createTextFile(dir,
437 name.startsWith(".") ? name : name + ".dart", 788 name.startsWith(".") ? name : name + ".dart",
438 content); 789 content);
439 } else { 790 } else {
440 assert(content is Map); 791 assert(content is Map);
441 var subdir = createDir(dir, name); 792 var subdir = createDir(dir, name);
442 createRecursive(subdir, content); 793 createRecursive(subdir, content);
443 } 794 }
444 } 795 }
445 } 796 }
446 797
447 createRecursive(tempDir, content); 798 createRecursive(createDir(tempDir, subDir), content);
448 if (packageDir != null) {
449 // Unused?
450 Map packages = content[packageDir];
451 var entries =
452 packages.keys.map((key) => "$key:$packageDir/$key").join("\n");
453 createTextFile(tempDir, ".packages", entries);
454 }
455 } 799 }
456 800
457 /// Start an HTTP server which serves a directory/file structure. 801 /// Start an HTTP server which serves a directory/file structure.
458 /// 802 ///
459 /// The directories and files are described by [files]. 803 /// The directories and files are described by [files].
460 /// 804 ///
461 /// Each map key is an entry in a directory. A `Map` value is a sub-directory 805 /// Each map key is an entry in a directory. A `Map` value is a sub-directory
462 /// and a `String` value is a text file. 806 /// and a `String` value is a text file.
463 /// The file contents are run through [fixPaths] to allow them to be self- 807 /// The file contents are run through [fixPaths] to allow them to be self-
464 /// referential. 808 /// referential.
465 Future<HttpServer> startServer(Map files, String fixPaths(String text)) async { 809 Future<HttpServer> startServer(Map files) async {
466 return (await HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, 0)) 810 return (await HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, 0))
467 ..forEach((request) { 811 ..forEach((request) {
468 var result = files; 812 var result = files;
469 onFailure: { 813 onFailure: {
470 for (var part in request.uri.pathSegments) { 814 for (var part in request.uri.pathSegments) {
471 if (part.endsWith(".dart")) { 815 if (part.endsWith(".dart")) {
472 part = part.substring(0, part.length - 5); 816 part = part.substring(0, part.length - 5);
473 } 817 }
474 if (result is Map) { 818 if (result is Map) {
475 result = result[part]; 819 result = result[part];
476 } else { 820 } else {
477 break onFailure; 821 break onFailure;
478 } 822 }
479 } 823 }
480 if (result is String) { 824 if (result is String) {
481 request.response..write(fixPaths(result)) 825 request.response..write(result)
482 ..close(); 826 ..close();
483 return; 827 return;
484 } 828 }
485 } 829 }
486 request.response..statusCode = HttpStatus.NOT_FOUND 830 request.response..statusCode = HttpStatus.NOT_FOUND
487 ..close(); 831 ..close();
488 }); 832 });
489 } 833 }
490 834
491 // Counter used to avoid reusing temporary directory names. 835 // Counter used to avoid reusing temporary file or directory names.
492 // Some platforms are timer based, and creating two temp-dirs withing a short 836 //
493 // duration may cause a collision. 837 // Used when adding extra files to an existing directory structure,
494 int tmpDirCounter = 0; 838 // and when creating temporary directories.
839 //
840 // Some platform temporary-directory implementations are timer based,
841 // and creating two temp-dirs withing a short duration may cause a collision.
842 int tmpNameCounter = 0;
843
844 // Fresh file name.
845 String freshName([String base = "tmp"]) => "$base${tmpNameCounter++}";
495 846
496 Directory createTempDir() { 847 Directory createTempDir() {
497 return Directory.systemTemp.createTempSync("pftest-${tmpDirCounter++}-"); 848 return Directory.systemTemp.createTempSync(freshName("pftest-"));
498 } 849 }
850
851 typedef void ConfigUpdate(Configuration configuration);
852
853 /// The configuration for a single test.
854 class Configuration {
855 /// The "description" of the test - a description of the set-up.
856 final String description;
857 /// The package root parameter passed to the Dart isolate.
858 ///
859 /// At most one of [root] and [config] should be supplied. If both are
860 /// omitted, a VM will search for a packages file or dir.
861 final String root;
862 /// The package configuration file location passed to the Dart isolate.
863 final String config;
864 /// Path to the main file to run.
865 final String mainFile;
866 /// List of arguments to pass to the main function.
867 final List<String> args;
868 /// The expected values for `Platform.package{Root,Config}`,
869 /// `Isolate.package{Root,Config}` and resolution of package URIs
870 /// in a `foo` package.
871 ///
872 /// The results are found by running the `main.dart` file inside [mainDir].
873 /// The tests can run this file after doing other `spawn` or `spawnUri` calls.
874 final Map expect;
875
876 Configuration({this.description,
877 this.root,
878 this.config,
879 this.mainFile,
880 this.args,
881 this.expect});
882
883 // Gets the type of main file, one of `main`, `spawnMain` or `spawnUriMain`.
884 String get mainType {
885 var lastSlash = mainFile.lastIndexOf("/");
886 if (lastSlash < 0) {
887 // Assume it's a Windows path.
888 lastSlash = mainFile.lastIndexOf(r"\");
889 }
890 var name = mainFile.substring(lastSlash + 1, mainFile.length - 5);
891 assert(name == "main" || name == "spawnMain" || name == "spawnUriMain");
892 return name;
893 }
894
895 String get mainPath {
896 var lastSlash = mainFile.lastIndexOf("/");
897 if (lastSlash < 0) {
898 // Assume it's a Windows path.
899 lastSlash = mainFile.lastIndexOf(r"\");
900 }
901 return mainFile.substring(0, lastSlash + 1);
902 }
903
904 /// Create a new configuration from the old one.
905 ///
906 /// [description] is new description.
907 ///
908 /// [main] is one of `main`, `spawnMain` or `spawnUriMain`, and changes
909 /// the [Configuration.mainFile] to a different file in the same directory.
910 ///
911 /// [mainFile] overrides [Configuration.mainFile] completely, and ignores
912 /// [main].
913 ///
914 /// [newArgs] are prepended to the existing [Configuration.args].
915 ///
916 /// [args] overrides [Configuration.args] completely and ignores [newArgs].
917 ///
918 /// [expect] overrides individual expectations.
919 ///
920 /// [root] and [config] overrides the existing values.
921 Configuration update({
922 String description,
923 String main,
924 String mainFile,
925 String root,
926 String config,
927 List<String> args,
928 List<String> newArgs,
929 Map expect
930 }) {
931 return new Configuration(
932 description: description ?? this.description,
933 root: root ?? this.root,
934 config: config ?? this.config,
935 mainFile: mainFile ??
936 ((main == null) ? this.mainFile : "${this.mainPath}$main.dart"),
937 args:
938 args ?? (<String>[]..addAll(newArgs ?? const <String>[])
939 ..addAll(this.args)),
940 expect: expect == null
941 ? this.expect
942 : new Map.from(this.expect)..addAll(expect ?? const {}));
943 }
944
945 // For debugging.
946 String toString() {
947 return "Configuration($description\n"
948 " root : $root\n"
949 " config: $config\n"
950 " main : $mainFile\n"
951 " args : ${args.map((x) => '"$x"').join(" ")}\n"
952 ") : expect {\n${expect.keys.map((k) =>
953 ' "$k"'.padRight(6) + ":${JSON.encode(expect[k])}\n").join()}"
954 "}";
955 }
956 }
957
958
959 // Inserts the file with generalized [name] at [path] with [content].
960 //
961 // The [path] is a directory where the file is created. It must start with
962 // either '%file/' or '%http/' to select the structure to put it into.
963 //
964 // The [name] should not have a trailing ".dart" for Dart files. Any file
965 // not starting with "." is assumed to be a ".dart" file.
966 void insertFileAt(Map file, Map http,
967 String path, String name, String content) {
968 var parts = path.split('/').toList();
969 var dir = (parts[0] == "%file") ? file : http;
970 for (var i = 1; i < parts.length - 1; i++) {
971 var entry = parts[i];
972 dir = dir[entry] ?? (dir[entry] = {});
973 }
974 dir[name] = content;
975 }
OLDNEW
« no previous file with comments | « runtime/bin/builtin.dart ('k') | tests/standalone/standalone.status » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698