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