Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 /** | 5 /** |
| 6 * Test infrastructure for testing pub. Unlike typical unit tests, most pub | 6 * Test infrastructure for testing pub. Unlike typical unit tests, most pub |
| 7 * tests are integration tests that stage some stuff on the file system, run | 7 * tests are integration tests that stage some stuff on the file system, run |
| 8 * pub, and then validate the results. This library provides an API to build | 8 * pub, and then validate the results. This library provides an API to build |
| 9 * tests like that. | 9 * tests like that. |
| 10 */ | 10 */ |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 53 * Creates a new [TarFileDescriptor] with [name] and [contents]. | 53 * Creates a new [TarFileDescriptor] with [name] and [contents]. |
| 54 */ | 54 */ |
| 55 TarFileDescriptor tar(Pattern name, [List<Descriptor> contents]) => | 55 TarFileDescriptor tar(Pattern name, [List<Descriptor> contents]) => |
| 56 new TarFileDescriptor(name, contents); | 56 new TarFileDescriptor(name, contents); |
| 57 | 57 |
| 58 /** | 58 /** |
| 59 * The current [HttpServer] created using [serve]. | 59 * The current [HttpServer] created using [serve]. |
| 60 */ | 60 */ |
| 61 var _server; | 61 var _server; |
| 62 | 62 |
| 63 /** The cached value for [_portCompleter]. */ | |
| 64 Completer<int> _portCompleterCache; | |
| 65 | |
| 66 /** The completer for [port]. */ | |
| 67 Completer<int> get _portCompleter() { | |
|
Bob Nystrom
2012/08/23 21:49:13
New getter syntax. :)
nweiz
2012/08/23 22:25:49
Done.
| |
| 68 if (_portCompleterCache != null) return _portCompleterCache; | |
| 69 _portCompleterCache = new Completer<int>(); | |
| 70 _scheduleCleanup((_) { | |
| 71 _portCompleterCache = null; | |
| 72 }); | |
| 73 return _portCompleterCache; | |
| 74 } | |
| 75 | |
| 76 /** | |
| 77 * A future that will complete to the port used for the current server. | |
| 78 */ | |
| 79 Future<int> get port() => _portCompleter.future; | |
| 80 | |
| 63 /** | 81 /** |
| 64 * Creates an HTTP server to serve [contents] as static files. This server will | 82 * Creates an HTTP server to serve [contents] as static files. This server will |
| 65 * exist only for the duration of the pub run. | 83 * exist only for the duration of the pub run. |
| 66 * | 84 * |
| 67 * Subsequent calls to [serve] will replace the previous server. | 85 * Subsequent calls to [serve] will replace the previous server. |
| 68 */ | 86 */ |
| 69 void serve(String host, int port, [List<Descriptor> contents]) { | 87 void serve([List<Descriptor> contents]) { |
| 70 var baseDir = dir("serve-dir", contents); | 88 var baseDir = dir("serve-dir", contents); |
| 71 if (host == 'localhost') { | |
| 72 host = '127.0.0.1'; | |
| 73 } | |
| 74 | 89 |
| 75 _schedule((_) { | 90 _schedule((_) { |
| 76 _closeServer().transform((_) { | 91 _closeServer().transform((resolvedPort) { |
| 77 _server = new HttpServer(); | 92 _server = new HttpServer(); |
| 78 _server.defaultRequestHandler = (request, response) { | 93 _server.defaultRequestHandler = (request, response) { |
| 79 var path = request.uri.replaceFirst("/", "").split("/"); | 94 var path = request.uri.replaceFirst("/", "").split("/"); |
| 80 response.persistentConnection = false; | 95 response.persistentConnection = false; |
| 81 var stream; | 96 var stream; |
| 82 try { | 97 try { |
| 83 stream = baseDir.load(path); | 98 stream = baseDir.load(path); |
| 84 } catch (var e) { | 99 } catch (var e) { |
| 85 response.statusCode = 404; | 100 response.statusCode = 404; |
| 86 response.contentLength = 0; | 101 response.contentLength = 0; |
| 87 response.outputStream.close(); | 102 response.outputStream.close(); |
| 88 return; | 103 return; |
| 89 } | 104 } |
| 90 | 105 |
| 91 var future = consumeInputStream(stream); | 106 var future = consumeInputStream(stream); |
| 92 future.then((data) { | 107 future.then((data) { |
| 93 response.statusCode = 200; | 108 response.statusCode = 200; |
| 94 response.contentLength = data.length; | 109 response.contentLength = data.length; |
| 95 response.outputStream.write(data); | 110 response.outputStream.write(data); |
| 96 response.outputStream.close(); | 111 response.outputStream.close(); |
| 97 }); | 112 }); |
| 98 | 113 |
| 99 future.handleException((e) { | 114 future.handleException((e) { |
| 100 print("Exception while handling ${request.uri}: $e"); | 115 print("Exception while handling ${request.uri}: $e"); |
| 101 response.statusCode = 500; | 116 response.statusCode = 500; |
| 102 response.reasonPhrase = e.message; | 117 response.reasonPhrase = e.message; |
| 103 response.outputStream.close(); | 118 response.outputStream.close(); |
| 104 }); | 119 }); |
| 105 }; | 120 }; |
| 106 _server.listen(host, port); | 121 _server.listen("127.0.0.1", 0); |
| 122 _portCompleter.complete(_server.port); | |
| 107 _scheduleCleanup((_) => _closeServer()); | 123 _scheduleCleanup((_) => _closeServer()); |
| 108 return null; | 124 return null; |
| 109 }); | 125 }); |
| 110 }); | 126 }); |
| 111 } | 127 } |
| 112 | 128 |
| 113 /** | 129 /** |
| 114 * Closes [_server]. Returns a [Future] that will complete after the [_server] | 130 * Closes [_server]. Returns a [Future] that will complete after the [_server] |
| 115 * is closed. | 131 * is closed. |
| 116 */ | 132 */ |
| 117 Future _closeServer() { | 133 Future _closeServer() { |
| 118 if (_server == null) return new Future.immediate(null); | 134 if (_server == null) return new Future.immediate(null); |
| 119 _server.close(); | 135 _server.close(); |
| 120 _server = null; | 136 _server = null; |
| 137 _portCompleterCache = null; | |
| 121 // TODO(nweiz): Remove this once issue 4155 is fixed. Pumping the event loop | 138 // TODO(nweiz): Remove this once issue 4155 is fixed. Pumping the event loop |
| 122 // *seems* to be enough to ensure that the server is actually closed, but I'm | 139 // *seems* to be enough to ensure that the server is actually closed, but I'm |
| 123 // putting this at 10ms to be safe. | 140 // putting this at 10ms to be safe. |
| 124 return sleep(10); | 141 return sleep(10); |
| 125 } | 142 } |
| 126 | 143 |
| 127 /** | 144 /** |
| 145 * The [DirectoryDescriptor] describing the server layout of packages that are | |
| 146 * being served via [servePackages]. This is `null` if [servePackages] has not | |
| 147 * yet been called for this test. | |
| 148 */ | |
| 149 DirectoryDescriptor _servedPackageDir; | |
| 150 | |
| 151 /** | |
| 152 * A map from package names to version numbers to YAML-serialized pubspecs for | |
| 153 * those packagesk. This represents the packages currently being served by | |
|
Bob Nystrom
2012/08/23 21:49:13
"packagesk" -> "packages".
nweiz
2012/08/23 22:25:49
Done.
| |
| 154 * [servePackages], and is `null` if [servePackages] has not yet been called for | |
| 155 * this test. | |
| 156 */ | |
| 157 Map<String, Map<String, String>> _servedPackages; | |
| 158 | |
| 159 /** | |
| 128 * Creates an HTTP server that replicates the structure of pub.dartlang.org. | 160 * Creates an HTTP server that replicates the structure of pub.dartlang.org. |
| 129 * [pubspecs] is a list of unserialized pubspecs representing the packages to | 161 * [pubspecs] is a list of unserialized pubspecs representing the packages to |
| 130 * serve. | 162 * serve. |
| 163 * | |
| 164 * Subsequent calls to [servePackages] will add to the set of packages that are | |
| 165 * being served. Previous packages will continue to be served. | |
| 131 */ | 166 */ |
| 132 void servePackages(String host, int port, List<Map> pubspecs) { | 167 void servePackages(List<Map> pubspecs) { |
| 133 var packages = <String, Map<String, String>>{}; | 168 if (_servedPackages == null || _servedPackageDir) { |
| 134 for (var spec in pubspecs) { | 169 _servedPackages = <String, Map<String, String>>{}; |
| 135 var name = spec['name']; | 170 _servedPackageDir = dir('packages', []); |
| 136 var version = spec['version']; | 171 serve([_servedPackageDir]); |
| 137 packages.putIfAbsent(name, () => <String, String>{})[version] = yaml(spec); | 172 |
| 173 _scheduleCleanup((_) { | |
| 174 _servedPackages = null; | |
| 175 _servedPackageDir = null; | |
| 176 }); | |
| 138 } | 177 } |
| 139 | 178 |
| 140 serve(host, port, [ | 179 _schedule((_) { |
| 141 dir('packages', flatten(packages.getKeys().map((name) { | 180 return Futures.wait(pubspecs.map(_awaitObject)) |
|
Bob Nystrom
2012/08/23 21:49:13
Can you just do _awaitObject(pubspects)?
nweiz
2012/08/23 22:25:49
Done.
| |
| 142 return [ | 181 .transform((resolvedPubspecs) { |
| 143 file('$name.json', | 182 for (var spec in resolvedPubspecs) { |
| 144 JSON.stringify({'versions': packages[name].getKeys()})), | 183 var name = spec['name']; |
| 145 dir(name, [ | 184 var version = spec['version']; |
| 146 dir('versions', flatten(packages[name].getKeys().map((version) { | 185 var versions = _servedPackages.putIfAbsent(name, () => <String, String>{ }); |
|
Bob Nystrom
2012/08/23 21:49:13
Long line.
nweiz
2012/08/23 22:25:49
Done.
| |
| 147 return [ | 186 versions[version] = yaml(spec); |
| 148 file('$version.yaml', packages[name][version]), | 187 } |
| 149 tar('$version.tar.gz', [ | 188 |
| 150 file('pubspec.yaml', packages[name][version]), | 189 _servedPackageDir.contents.clear(); |
| 151 file('$name.dart', 'main() => print("$name $version");') | 190 for (var name in _servedPackages.getKeys()) { |
| 152 ]) | 191 _servedPackageDir.contents.addAll([ |
| 153 ]; | 192 file('$name.json', |
| 154 }))) | 193 JSON.stringify({'versions': _servedPackages[name].getKeys()})), |
|
Bob Nystrom
2012/08/23 21:49:13
+2
nweiz
2012/08/23 22:25:49
Done.
| |
| 155 ]) | 194 dir(name, [ |
| 156 ]; | 195 dir('versions', flatten(_servedPackages[name].getKeys().map((version ) { |
|
Bob Nystrom
2012/08/23 21:49:13
Lone line.
nweiz
2012/08/23 22:25:49
Done.
| |
| 157 }))) | 196 return [ |
| 158 ]); | 197 file('$version.yaml', _servedPackages[name][version]), |
| 198 tar('$version.tar.gz', [ | |
| 199 file('pubspec.yaml', _servedPackages[name][version]), | |
| 200 file('$name.dart', 'main() => print("$name $version");') | |
| 201 ]) | |
| 202 ]; | |
| 203 }))) | |
| 204 ]) | |
| 205 ]); | |
| 206 } | |
| 207 }); | |
| 208 }); | |
| 159 } | 209 } |
| 160 | 210 |
| 161 /** Converts [value] into a YAML string. */ | 211 /** Converts [value] into a YAML string. */ |
| 162 String yaml(value) => JSON.stringify(value); | 212 String yaml(value) => JSON.stringify(value); |
| 163 | 213 |
| 164 /** | 214 /** |
| 165 * Describes a file named `pubspec.yaml` with the given YAML-serialized | 215 * Describes a file named `pubspec.yaml` with the given YAML-serialized |
| 166 * [contents], which should be a serializable object. | 216 * [contents], which should be a serializable object. |
| 167 * | 217 * |
| 168 * [contents] may contain [Future]s that resolve to serializable objects, which | 218 * [contents] may contain [Future]s that resolve to serializable objects, which |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 197 package["dependencies"] = _dependencyListToMap(dependencies); | 247 package["dependencies"] = _dependencyListToMap(dependencies); |
| 198 } | 248 } |
| 199 return package; | 249 return package; |
| 200 } | 250 } |
| 201 | 251 |
| 202 /** | 252 /** |
| 203 * Describes a map representing a dependency on a package in the package | 253 * Describes a map representing a dependency on a package in the package |
| 204 * repository. | 254 * repository. |
| 205 */ | 255 */ |
| 206 Map dependency(String name, [String versionConstraint]) { | 256 Map dependency(String name, [String versionConstraint]) { |
| 207 var dependency = {"repo": {"name": name, "url": "http://localhost:3123"}}; | 257 var url = port.transform((p) => "http://localhost:$p"); |
| 258 var dependency = {"repo": {"name": name, "url": url}}; | |
| 208 if (versionConstraint != null) dependency["version"] = versionConstraint; | 259 if (versionConstraint != null) dependency["version"] = versionConstraint; |
| 209 return dependency; | 260 return dependency; |
| 210 } | 261 } |
| 211 | 262 |
| 212 /** | 263 /** |
| 213 * Describes a directory for a package installed from the mock package repo. | 264 * Describes a directory for a package installed from the mock package repo. |
| 214 * This directory is of the form found in the `packages/` directory. | 265 * This directory is of the form found in the `packages/` directory. |
| 215 */ | 266 */ |
| 216 DirectoryDescriptor packageDir(String name, String version) { | 267 DirectoryDescriptor packageDir(String name, String version) { |
| 217 return dir(name, [ | 268 return dir(name, [ |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 264 */ | 315 */ |
| 265 DirectoryDescriptor cacheDir(Map packages) { | 316 DirectoryDescriptor cacheDir(Map packages) { |
| 266 var contents = <Map>[]; | 317 var contents = <Map>[]; |
| 267 packages.forEach((name, versions) { | 318 packages.forEach((name, versions) { |
| 268 if (versions is! List) versions = [versions]; | 319 if (versions is! List) versions = [versions]; |
| 269 for (var version in versions) { | 320 for (var version in versions) { |
| 270 contents.add(packageCacheDir(name, version)); | 321 contents.add(packageCacheDir(name, version)); |
| 271 } | 322 } |
| 272 }); | 323 }); |
| 273 return dir(cachePath, [ | 324 return dir(cachePath, [ |
| 274 dir('repo', [dir('localhost%583123', contents)]) | 325 dir('repo', [ |
| 326 async(port.transform((p) => dir('localhost%58$p', contents))) | |
| 327 ]) | |
| 275 ]); | 328 ]); |
| 276 } | 329 } |
| 277 | 330 |
| 278 /** | 331 /** |
| 279 * Describes the application directory, containing only a pubspec specifying the | 332 * Describes the application directory, containing only a pubspec specifying the |
| 280 * given [dependencies]. | 333 * given [dependencies]. |
| 281 */ | 334 */ |
| 282 DirectoryDescriptor appDir(List dependencies) => | 335 DirectoryDescriptor appDir(List dependencies) => |
| 283 dir(appPath, [appPubspec(dependencies)]); | 336 dir(appPath, [appPubspec(dependencies)]); |
| 284 | 337 |
| (...skipping 698 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 983 } | 1036 } |
| 984 | 1037 |
| 985 /** | 1038 /** |
| 986 * Schedules a callback to be called after Pub is run with [runPub], even if it | 1039 * Schedules a callback to be called after Pub is run with [runPub], even if it |
| 987 * fails. | 1040 * fails. |
| 988 */ | 1041 */ |
| 989 void _scheduleCleanup(_ScheduledEvent event) { | 1042 void _scheduleCleanup(_ScheduledEvent event) { |
| 990 if (_scheduledCleanup == null) _scheduledCleanup = []; | 1043 if (_scheduledCleanup == null) _scheduledCleanup = []; |
| 991 _scheduledCleanup.add(event); | 1044 _scheduledCleanup.add(event); |
| 992 } | 1045 } |
| OLD | NEW |