| 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 |