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

Side by Side Diff: utils/tests/pub/test_pub.dart

Issue 11785008: Convert pub tests to line doc comments. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | utils/tests/pub/version_solver_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 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 /// Test infrastructure for testing pub. Unlike typical unit tests, most pub
6 * Test infrastructure for testing pub. Unlike typical unit tests, most pub 6 /// 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 7 /// 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 8 /// tests like that.
9 * tests like that.
10 */
11 library test_pub; 9 library test_pub;
12 10
13 import 'dart:io'; 11 import 'dart:io';
14 import 'dart:isolate'; 12 import 'dart:isolate';
15 import 'dart:json'; 13 import 'dart:json';
16 import 'dart:math'; 14 import 'dart:math';
17 import 'dart:uri'; 15 import 'dart:uri';
18 16
19 import '../../../pkg/oauth2/lib/oauth2.dart' as oauth2; 17 import '../../../pkg/oauth2/lib/oauth2.dart' as oauth2;
20 import '../../../pkg/path/lib/path.dart' as path; 18 import '../../../pkg/path/lib/path.dart' as path;
21 import '../../../pkg/unittest/lib/unittest.dart'; 19 import '../../../pkg/unittest/lib/unittest.dart';
22 import '../../../pkg/http/lib/testing.dart'; 20 import '../../../pkg/http/lib/testing.dart';
23 import '../../lib/file_system.dart' as fs; 21 import '../../lib/file_system.dart' as fs;
24 import '../../pub/entrypoint.dart'; 22 import '../../pub/entrypoint.dart';
25 import '../../pub/git_source.dart'; 23 import '../../pub/git_source.dart';
26 import '../../pub/hosted_source.dart'; 24 import '../../pub/hosted_source.dart';
27 import '../../pub/http.dart'; 25 import '../../pub/http.dart';
28 import '../../pub/io.dart'; 26 import '../../pub/io.dart';
29 import '../../pub/sdk_source.dart'; 27 import '../../pub/sdk_source.dart';
30 import '../../pub/system_cache.dart'; 28 import '../../pub/system_cache.dart';
31 import '../../pub/utils.dart'; 29 import '../../pub/utils.dart';
32 import '../../pub/validator.dart'; 30 import '../../pub/validator.dart';
33 import '../../pub/yaml/yaml.dart'; 31 import '../../pub/yaml/yaml.dart';
34 32
35 /** 33 /// Creates a new [FileDescriptor] with [name] and [contents].
36 * Creates a new [FileDescriptor] with [name] and [contents].
37 */
38 FileDescriptor file(Pattern name, String contents) => 34 FileDescriptor file(Pattern name, String contents) =>
39 new FileDescriptor(name, contents); 35 new FileDescriptor(name, contents);
40 36
41 /** 37 /// Creates a new [DirectoryDescriptor] with [name] and [contents].
42 * Creates a new [DirectoryDescriptor] with [name] and [contents].
43 */
44 DirectoryDescriptor dir(Pattern name, [List<Descriptor> contents]) => 38 DirectoryDescriptor dir(Pattern name, [List<Descriptor> contents]) =>
45 new DirectoryDescriptor(name, contents); 39 new DirectoryDescriptor(name, contents);
46 40
47 /** 41 /// Creates a new [FutureDescriptor] wrapping [future].
48 * Creates a new [FutureDescriptor] wrapping [future].
49 */
50 FutureDescriptor async(Future<Descriptor> future) => 42 FutureDescriptor async(Future<Descriptor> future) =>
51 new FutureDescriptor(future); 43 new FutureDescriptor(future);
52 44
53 /** 45 /// Creates a new [GitRepoDescriptor] with [name] and [contents].
54 * Creates a new [GitRepoDescriptor] with [name] and [contents].
55 */
56 GitRepoDescriptor git(Pattern name, [List<Descriptor> contents]) => 46 GitRepoDescriptor git(Pattern name, [List<Descriptor> contents]) =>
57 new GitRepoDescriptor(name, contents); 47 new GitRepoDescriptor(name, contents);
58 48
59 /** 49 /// Creates a new [TarFileDescriptor] with [name] and [contents].
60 * Creates a new [TarFileDescriptor] with [name] and [contents].
61 */
62 TarFileDescriptor tar(Pattern name, [List<Descriptor> contents]) => 50 TarFileDescriptor tar(Pattern name, [List<Descriptor> contents]) =>
63 new TarFileDescriptor(name, contents); 51 new TarFileDescriptor(name, contents);
64 52
65 /** 53 /// Creates a new [NothingDescriptor] with [name].
66 * Creates a new [NothingDescriptor] with [name].
67 */
68 NothingDescriptor nothing(String name) => new NothingDescriptor(name); 54 NothingDescriptor nothing(String name) => new NothingDescriptor(name);
69 55
70 /** 56 /// The current [HttpServer] created using [serve].
71 * The current [HttpServer] created using [serve].
72 */
73 var _server; 57 var _server;
74 58
75 /** The cached value for [_portCompleter]. */ 59 /// The cached value for [_portCompleter].
76 Completer<int> _portCompleterCache; 60 Completer<int> _portCompleterCache;
77 61
78 /** The completer for [port]. */ 62 /// The completer for [port].
79 Completer<int> get _portCompleter { 63 Completer<int> get _portCompleter {
80 if (_portCompleterCache != null) return _portCompleterCache; 64 if (_portCompleterCache != null) return _portCompleterCache;
81 _portCompleterCache = new Completer<int>(); 65 _portCompleterCache = new Completer<int>();
82 _scheduleCleanup((_) { 66 _scheduleCleanup((_) {
83 _portCompleterCache = null; 67 _portCompleterCache = null;
84 }); 68 });
85 return _portCompleterCache; 69 return _portCompleterCache;
86 } 70 }
87 71
88 /** 72 /// A future that will complete to the port used for the current server.
89 * A future that will complete to the port used for the current server.
90 */
91 Future<int> get port => _portCompleter.future; 73 Future<int> get port => _portCompleter.future;
92 74
93 /** 75 /// Creates an HTTP server to serve [contents] as static files. This server will
94 * Creates an HTTP server to serve [contents] as static files. This server will 76 /// exist only for the duration of the pub run.
95 * exist only for the duration of the pub run. 77 ///
96 * 78 /// Subsequent calls to [serve] will replace the previous server.
97 * Subsequent calls to [serve] will replace the previous server.
98 */
99 void serve([List<Descriptor> contents]) { 79 void serve([List<Descriptor> contents]) {
100 var baseDir = dir("serve-dir", contents); 80 var baseDir = dir("serve-dir", contents);
101 81
102 _schedule((_) { 82 _schedule((_) {
103 return _closeServer().transform((_) { 83 return _closeServer().transform((_) {
104 _server = new HttpServer(); 84 _server = new HttpServer();
105 _server.defaultRequestHandler = (request, response) { 85 _server.defaultRequestHandler = (request, response) {
106 var path = request.uri.replaceFirst("/", "").split("/"); 86 var path = request.uri.replaceFirst("/", "").split("/");
107 response.persistentConnection = false; 87 response.persistentConnection = false;
108 var stream; 88 var stream;
(...skipping 22 matching lines...) Expand all
131 }); 111 });
132 }; 112 };
133 _server.listen("127.0.0.1", 0); 113 _server.listen("127.0.0.1", 0);
134 _portCompleter.complete(_server.port); 114 _portCompleter.complete(_server.port);
135 _scheduleCleanup((_) => _closeServer()); 115 _scheduleCleanup((_) => _closeServer());
136 return null; 116 return null;
137 }); 117 });
138 }); 118 });
139 } 119 }
140 120
141 /** 121 /// Closes [_server]. Returns a [Future] that will complete after the [_server]
142 * Closes [_server]. Returns a [Future] that will complete after the [_server] 122 /// is closed.
143 * is closed.
144 */
145 Future _closeServer() { 123 Future _closeServer() {
146 if (_server == null) return new Future.immediate(null); 124 if (_server == null) return new Future.immediate(null);
147 _server.close(); 125 _server.close();
148 _server = null; 126 _server = null;
149 _portCompleterCache = null; 127 _portCompleterCache = null;
150 // TODO(nweiz): Remove this once issue 4155 is fixed. Pumping the event loop 128 // TODO(nweiz): Remove this once issue 4155 is fixed. Pumping the event loop
151 // *seems* to be enough to ensure that the server is actually closed, but I'm 129 // *seems* to be enough to ensure that the server is actually closed, but I'm
152 // putting this at 10ms to be safe. 130 // putting this at 10ms to be safe.
153 return sleep(10); 131 return sleep(10);
154 } 132 }
155 133
156 /** 134 /// The [DirectoryDescriptor] describing the server layout of packages that are
157 * The [DirectoryDescriptor] describing the server layout of packages that are 135 /// being served via [servePackages]. This is `null` if [servePackages] has not
158 * being served via [servePackages]. This is `null` if [servePackages] has not 136 /// yet been called for this test.
159 * yet been called for this test.
160 */
161 DirectoryDescriptor _servedPackageDir; 137 DirectoryDescriptor _servedPackageDir;
162 138
163 /** 139 /// A map from package names to version numbers to YAML-serialized pubspecs for
164 * A map from package names to version numbers to YAML-serialized pubspecs for 140 /// those packages. This represents the packages currently being served by
165 * those packages. This represents the packages currently being served by 141 /// [servePackages], and is `null` if [servePackages] has not yet been called
166 * [servePackages], and is `null` if [servePackages] has not yet been called for 142 /// for this test.
167 * this test.
168 */
169 Map<String, Map<String, String>> _servedPackages; 143 Map<String, Map<String, String>> _servedPackages;
170 144
171 /** 145 /// Creates an HTTP server that replicates the structure of pub.dartlang.org.
172 * Creates an HTTP server that replicates the structure of pub.dartlang.org. 146 /// [pubspecs] is a list of unserialized pubspecs representing the packages to
173 * [pubspecs] is a list of unserialized pubspecs representing the packages to 147 /// serve.
174 * serve. 148 ///
175 * 149 /// Subsequent calls to [servePackages] will add to the set of packages that
176 * Subsequent calls to [servePackages] will add to the set of packages that are 150 /// are being served. Previous packages will continue to be served.
177 * being served. Previous packages will continue to be served.
178 */
179 void servePackages(List<Map> pubspecs) { 151 void servePackages(List<Map> pubspecs) {
180 if (_servedPackages == null || _servedPackageDir == null) { 152 if (_servedPackages == null || _servedPackageDir == null) {
181 _servedPackages = <String, Map<String, String>>{}; 153 _servedPackages = <String, Map<String, String>>{};
182 _servedPackageDir = dir('packages', []); 154 _servedPackageDir = dir('packages', []);
183 serve([_servedPackageDir]); 155 serve([_servedPackageDir]);
184 156
185 _scheduleCleanup((_) { 157 _scheduleCleanup((_) {
186 _servedPackages = null; 158 _servedPackages = null;
187 _servedPackageDir = null; 159 _servedPackageDir = null;
188 }); 160 });
(...skipping 25 matching lines...) Expand all
214 ]) 186 ])
215 ]; 187 ];
216 }))) 188 })))
217 ]) 189 ])
218 ]); 190 ]);
219 } 191 }
220 }); 192 });
221 }); 193 });
222 } 194 }
223 195
224 /** Converts [value] into a YAML string. */ 196 /// Converts [value] into a YAML string.
225 String yaml(value) => JSON.stringify(value); 197 String yaml(value) => JSON.stringify(value);
226 198
227 /// Describes a package that passes all validation. 199 /// Describes a package that passes all validation.
228 Descriptor get normalPackage => dir(appPath, [ 200 Descriptor get normalPackage => dir(appPath, [
229 libPubspec("test_pkg", "1.0.0"), 201 libPubspec("test_pkg", "1.0.0"),
230 file("LICENSE", "Eh, do what you want."), 202 file("LICENSE", "Eh, do what you want."),
231 dir("lib", [ 203 dir("lib", [
232 file("test_pkg.dart", "int i = 1;") 204 file("test_pkg.dart", "int i = 1;")
233 ]) 205 ])
234 ]); 206 ]);
235 207
236 /** 208 /// Describes a file named `pubspec.yaml` with the given YAML-serialized
237 * Describes a file named `pubspec.yaml` with the given YAML-serialized 209 /// [contents], which should be a serializable object.
238 * [contents], which should be a serializable object. 210 ///
239 * 211 /// [contents] may contain [Future]s that resolve to serializable objects,
240 * [contents] may contain [Future]s that resolve to serializable objects, which 212 /// which may in turn contain [Future]s recursively.
241 * may in turn contain [Future]s recursively.
242 */
243 Descriptor pubspec(Map contents) { 213 Descriptor pubspec(Map contents) {
244 return async(_awaitObject(contents).transform((resolvedContents) => 214 return async(_awaitObject(contents).transform((resolvedContents) =>
245 file("pubspec.yaml", yaml(resolvedContents)))); 215 file("pubspec.yaml", yaml(resolvedContents))));
246 } 216 }
247 217
248 /** 218 /// Describes a file named `pubspec.yaml` for an application package with the
249 * Describes a file named `pubspec.yaml` for an application package with the 219 /// given [dependencies].
250 * given [dependencies].
251 */
252 Descriptor appPubspec(List dependencies) { 220 Descriptor appPubspec(List dependencies) {
253 return pubspec({ 221 return pubspec({
254 "name": "myapp", 222 "name": "myapp",
255 "dependencies": _dependencyListToMap(dependencies) 223 "dependencies": _dependencyListToMap(dependencies)
256 }); 224 });
257 } 225 }
258 226
259 /** 227 /// Describes a file named `pubspec.yaml` for a library package with the given
260 * Describes a file named `pubspec.yaml` for a library package with the given 228 /// [name], [version], and [dependencies].
261 * [name], [version], and [dependencies].
262 */
263 Descriptor libPubspec(String name, String version, [List dependencies]) => 229 Descriptor libPubspec(String name, String version, [List dependencies]) =>
264 pubspec(package(name, version, dependencies)); 230 pubspec(package(name, version, dependencies));
265 231
266 /** 232 /// Describes a directory named `lib` containing a single dart file named
267 * Describes a directory named `lib` containing a single dart file named 233 /// `<name>.dart` that contains a line of Dart code.
268 * `<name>.dart` that contains a line of Dart code.
269 */
270 Descriptor libDir(String name, [String code]) { 234 Descriptor libDir(String name, [String code]) {
271 // Default to printing the name if no other code was given. 235 // Default to printing the name if no other code was given.
272 if (code == null) { 236 if (code == null) {
273 code = name; 237 code = name;
274 } 238 }
275 239
276 return dir("lib", [ 240 return dir("lib", [
277 file("$name.dart", 'main() => "$code";') 241 file("$name.dart", 'main() => "$code";')
278 ]); 242 ]);
279 } 243 }
280 244
281 /** 245 /// Describes a map representing a library package with the given [name],
282 * Describes a map representing a library package with the given [name], 246 /// [version], and [dependencies].
283 * [version], and [dependencies].
284 */
285 Map package(String name, String version, [List dependencies]) { 247 Map package(String name, String version, [List dependencies]) {
286 var package = { 248 var package = {
287 "name": name, 249 "name": name,
288 "version": version, 250 "version": version,
289 "author": "Nathan Weizenbaum <nweiz@google.com>", 251 "author": "Nathan Weizenbaum <nweiz@google.com>",
290 "homepage": "http://pub.dartlang.org", 252 "homepage": "http://pub.dartlang.org",
291 "description": "A package, I guess." 253 "description": "A package, I guess."
292 }; 254 };
293 if (dependencies != null) { 255 if (dependencies != null) {
294 package["dependencies"] = _dependencyListToMap(dependencies); 256 package["dependencies"] = _dependencyListToMap(dependencies);
295 } 257 }
296 return package; 258 return package;
297 } 259 }
298 260
299 /** 261 /// Describes a map representing a dependency on a package in the package
300 * Describes a map representing a dependency on a package in the package 262 /// repository.
301 * repository.
302 */
303 Map dependency(String name, [String versionConstraint]) { 263 Map dependency(String name, [String versionConstraint]) {
304 var url = port.transform((p) => "http://localhost:$p"); 264 var url = port.transform((p) => "http://localhost:$p");
305 var dependency = {"hosted": {"name": name, "url": url}}; 265 var dependency = {"hosted": {"name": name, "url": url}};
306 if (versionConstraint != null) dependency["version"] = versionConstraint; 266 if (versionConstraint != null) dependency["version"] = versionConstraint;
307 return dependency; 267 return dependency;
308 } 268 }
309 269
310 /** 270 /// Describes a directory for a package installed from the mock package server.
311 * Describes a directory for a package installed from the mock package server. 271 /// This directory is of the form found in the global package cache.
312 * This directory is of the form found in the global package cache.
313 */
314 DirectoryDescriptor packageCacheDir(String name, String version) { 272 DirectoryDescriptor packageCacheDir(String name, String version) {
315 return dir("$name-$version", [ 273 return dir("$name-$version", [
316 libDir(name, '$name $version') 274 libDir(name, '$name $version')
317 ]); 275 ]);
318 } 276 }
319 277
320 /** 278 /// Describes a directory for a Git package. This directory is of the form
321 * Describes a directory for a Git package. This directory is of the form found 279 /// found in the revision cache of the global package cache.
322 * in the revision cache of the global package cache.
323 */
324 DirectoryDescriptor gitPackageRevisionCacheDir(String name, [int modifier]) { 280 DirectoryDescriptor gitPackageRevisionCacheDir(String name, [int modifier]) {
325 var value = name; 281 var value = name;
326 if (modifier != null) value = "$name $modifier"; 282 if (modifier != null) value = "$name $modifier";
327 return dir(new RegExp("$name${r'-[a-f0-9]+'}"), [ 283 return dir(new RegExp("$name${r'-[a-f0-9]+'}"), [
328 libDir(name, value) 284 libDir(name, value)
329 ]); 285 ]);
330 } 286 }
331 287
332 /** 288 /// Describes a directory for a Git package. This directory is of the form
333 * Describes a directory for a Git package. This directory is of the form found 289 /// found in the repo cache of the global package cache.
334 * in the repo cache of the global package cache.
335 */
336 DirectoryDescriptor gitPackageRepoCacheDir(String name) { 290 DirectoryDescriptor gitPackageRepoCacheDir(String name) {
337 return dir(new RegExp("$name${r'-[a-f0-9]+'}"), [ 291 return dir(new RegExp("$name${r'-[a-f0-9]+'}"), [
338 dir('hooks'), 292 dir('hooks'),
339 dir('info'), 293 dir('info'),
340 dir('objects'), 294 dir('objects'),
341 dir('refs') 295 dir('refs')
342 ]); 296 ]);
343 } 297 }
344 298
345 /** 299 /// Describes the `packages/` directory containing all the given [packages],
346 * Describes the `packages/` directory containing all the given [packages], 300 /// which should be name/version pairs. The packages will be validated against
347 * which should be name/version pairs. The packages will be validated against 301 /// the format produced by the mock package server.
348 * the format produced by the mock package server. 302 ///
349 * 303 /// A package with a null version should not be installed.
350 * A package with a null version should not be installed.
351 */
352 DirectoryDescriptor packagesDir(Map<String, String> packages) { 304 DirectoryDescriptor packagesDir(Map<String, String> packages) {
353 var contents = <Descriptor>[]; 305 var contents = <Descriptor>[];
354 packages.forEach((name, version) { 306 packages.forEach((name, version) {
355 if (version == null) { 307 if (version == null) {
356 contents.add(nothing(name)); 308 contents.add(nothing(name));
357 } else { 309 } else {
358 contents.add(dir(name, [ 310 contents.add(dir(name, [
359 file("$name.dart", 'main() => "$name $version";') 311 file("$name.dart", 'main() => "$name $version";')
360 ])); 312 ]));
361 } 313 }
362 }); 314 });
363 return dir(packagesPath, contents); 315 return dir(packagesPath, contents);
364 } 316 }
365 317
366 /** 318 /// Describes the global package cache directory containing all the given
367 * Describes the global package cache directory containing all the given 319 /// [packages], which should be name/version pairs. The packages will be
368 * [packages], which should be name/version pairs. The packages will be 320 /// validated against the format produced by the mock package server.
369 * validated against the format produced by the mock package server. 321 ///
370 * 322 /// A package's value may also be a list of versions, in which case all
371 * A package's value may also be a list of versions, in which case all versions 323 /// versions are expected to be installed.
372 * are expected to be installed.
373 */
374 DirectoryDescriptor cacheDir(Map packages) { 324 DirectoryDescriptor cacheDir(Map packages) {
375 var contents = <Descriptor>[]; 325 var contents = <Descriptor>[];
376 packages.forEach((name, versions) { 326 packages.forEach((name, versions) {
377 if (versions is! List) versions = [versions]; 327 if (versions is! List) versions = [versions];
378 for (var version in versions) { 328 for (var version in versions) {
379 contents.add(packageCacheDir(name, version)); 329 contents.add(packageCacheDir(name, version));
380 } 330 }
381 }); 331 });
382 return dir(cachePath, [ 332 return dir(cachePath, [
383 dir('hosted', [ 333 dir('hosted', [
(...skipping 15 matching lines...) Expand all
399 file('credentials.json', new oauth2.Credentials( 349 file('credentials.json', new oauth2.Credentials(
400 accessToken, 350 accessToken,
401 refreshToken, 351 refreshToken,
402 url.resolve('/token'), 352 url.resolve('/token'),
403 ['https://www.googleapis.com/auth/userinfo.email'], 353 ['https://www.googleapis.com/auth/userinfo.email'],
404 expiration).toJson()) 354 expiration).toJson())
405 ]); 355 ]);
406 })); 356 }));
407 } 357 }
408 358
409 /** 359 /// Describes the application directory, containing only a pubspec specifying
410 * Describes the application directory, containing only a pubspec specifying the 360 /// the given [dependencies].
411 * given [dependencies].
412 */
413 DirectoryDescriptor appDir(List dependencies) => 361 DirectoryDescriptor appDir(List dependencies) =>
414 dir(appPath, [appPubspec(dependencies)]); 362 dir(appPath, [appPubspec(dependencies)]);
415 363
416 /** 364 /// Converts a list of dependencies as passed to [package] into a hash as used
417 * Converts a list of dependencies as passed to [package] into a hash as used in 365 /// in a pubspec.
418 * a pubspec.
419 */
420 Future<Map> _dependencyListToMap(List<Map> dependencies) { 366 Future<Map> _dependencyListToMap(List<Map> dependencies) {
421 return _awaitObject(dependencies).transform((resolvedDependencies) { 367 return _awaitObject(dependencies).transform((resolvedDependencies) {
422 var result = <String, Map>{}; 368 var result = <String, Map>{};
423 for (var dependency in resolvedDependencies) { 369 for (var dependency in resolvedDependencies) {
424 var keys = dependency.keys.filter((key) => key != "version"); 370 var keys = dependency.keys.filter((key) => key != "version");
425 var sourceName = only(keys); 371 var sourceName = only(keys);
426 var source; 372 var source;
427 switch (sourceName) { 373 switch (sourceName) {
428 case "git": 374 case "git":
429 source = new GitSource(); 375 source = new GitSource();
(...skipping 24 matching lines...) Expand all
454 case "hosted": 400 case "hosted":
455 if (description is String) return description; 401 if (description is String) return description;
456 return description['name']; 402 return description['name'];
457 case "sdk": 403 case "sdk":
458 return description; 404 return description;
459 default: 405 default:
460 return description; 406 return description;
461 } 407 }
462 } 408 }
463 409
464 /** 410 /// The path of the package cache directory used for tests. Relative to the
465 * The path of the package cache directory used for tests. Relative to the 411 /// sandbox directory.
466 * sandbox directory.
467 */
468 final String cachePath = "cache"; 412 final String cachePath = "cache";
469 413
470 /** 414 /// The path of the mock SDK directory used for tests. Relative to the sandbox
471 * The path of the mock SDK directory used for tests. Relative to the sandbox 415 /// directory.
472 * directory.
473 */
474 final String sdkPath = "sdk"; 416 final String sdkPath = "sdk";
475 417
476 /** 418 /// The path of the mock app directory used for tests. Relative to the sandbox
477 * The path of the mock app directory used for tests. Relative to the sandbox 419 /// directory.
478 * directory.
479 */
480 final String appPath = "myapp"; 420 final String appPath = "myapp";
481 421
482 /** 422 /// The path of the packages directory in the mock app used for tests. Relative
483 * The path of the packages directory in the mock app used for tests. Relative 423 /// to the sandbox directory.
484 * to the sandbox directory.
485 */
486 final String packagesPath = "$appPath/packages"; 424 final String packagesPath = "$appPath/packages";
487 425
488 /** 426 /// The type for callbacks that will be fired during [runPub]. Takes the
489 * The type for callbacks that will be fired during [runPub]. Takes the sandbox 427 /// sandbox directory as a parameter.
490 * directory as a parameter.
491 */
492 typedef Future _ScheduledEvent(Directory parentDir); 428 typedef Future _ScheduledEvent(Directory parentDir);
493 429
494 /** 430 /// The list of events that are scheduled to run as part of the test case.
495 * The list of events that are scheduled to run as part of the test case.
496 */
497 List<_ScheduledEvent> _scheduled; 431 List<_ScheduledEvent> _scheduled;
498 432
499 /** 433 /// The list of events that are scheduled to run after the test case, even if
500 * The list of events that are scheduled to run after the test case, even if it 434 /// it failed.
501 * failed.
502 */
503 List<_ScheduledEvent> _scheduledCleanup; 435 List<_ScheduledEvent> _scheduledCleanup;
504 436
505 /// The list of events that are scheduled to run after the test case only if it 437 /// The list of events that are scheduled to run after the test case only if it
506 /// failed. 438 /// failed.
507 List<_ScheduledEvent> _scheduledOnException; 439 List<_ScheduledEvent> _scheduledOnException;
508 440
509 /** 441 /// Set to true when the current batch of scheduled events should be aborted.
510 * Set to true when the current batch of scheduled events should be aborted.
511 */
512 bool _abortScheduled = false; 442 bool _abortScheduled = false;
513 443
514 /// The time (in milliseconds) to wait for the entire scheduled test to 444 /// The time (in milliseconds) to wait for the entire scheduled test to
515 /// complete. 445 /// complete.
516 final _TIMEOUT = 30000; 446 final _TIMEOUT = 30000;
517 447
518 /** 448 /// Runs all the scheduled events for a test case. This should only be called
519 * Runs all the scheduled events for a test case. This should only be called 449 /// once per test case.
520 * once per test case.
521 */
522 void run() { 450 void run() {
523 var createdSandboxDir; 451 var createdSandboxDir;
524 452
525 var asyncDone = expectAsync0(() {}); 453 var asyncDone = expectAsync0(() {});
526 454
527 Future cleanup() { 455 Future cleanup() {
528 return _runScheduled(createdSandboxDir, _scheduledCleanup).chain((_) { 456 return _runScheduled(createdSandboxDir, _scheduledCleanup).chain((_) {
529 _scheduled = null; 457 _scheduled = null;
530 _scheduledCleanup = null; 458 _scheduledCleanup = null;
531 _scheduledOnException = null; 459 _scheduledOnException = null;
(...skipping 21 matching lines...) Expand all
553 }); 481 });
554 subFuture.then((_) => registerException(error, future.stackTrace)); 482 subFuture.then((_) => registerException(error, future.stackTrace));
555 return true; 483 return true;
556 }); 484 });
557 485
558 timeout(future, _TIMEOUT, 'waiting for a test to complete') 486 timeout(future, _TIMEOUT, 'waiting for a test to complete')
559 .chain((_) => cleanup()) 487 .chain((_) => cleanup())
560 .then((_) => asyncDone()); 488 .then((_) => asyncDone());
561 } 489 }
562 490
563 /// Get the path to the root "util/test/pub" directory containing the pub tests. 491 /// Get the path to the root "util/test/pub" directory containing the pub
492 /// tests.
564 String get testDirectory { 493 String get testDirectory {
565 var dir = new Options().script; 494 var dir = new Options().script;
566 while (basename(dir) != 'pub') dir = dirname(dir); 495 while (basename(dir) != 'pub') dir = dirname(dir);
567 496
568 return getFullPath(dir); 497 return getFullPath(dir);
569 } 498 }
570 499
571 /** 500 /// Schedules a call to the Pub command-line utility. Runs Pub with [args] and
572 * Schedules a call to the Pub command-line utility. Runs Pub with [args] and 501 /// validates that its results match [output], [error], and [exitCode].
573 * validates that its results match [output], [error], and [exitCode].
574 */
575 void schedulePub({List args, Pattern output, Pattern error, 502 void schedulePub({List args, Pattern output, Pattern error,
576 Future<Uri> tokenEndpoint, int exitCode: 0}) { 503 Future<Uri> tokenEndpoint, int exitCode: 0}) {
577 _schedule((sandboxDir) { 504 _schedule((sandboxDir) {
578 return _doPub(runProcess, sandboxDir, args, tokenEndpoint) 505 return _doPub(runProcess, sandboxDir, args, tokenEndpoint)
579 .transform((result) { 506 .transform((result) {
580 var failures = []; 507 var failures = [];
581 508
582 _validateOutput(failures, 'stdout', output, result.stdout); 509 _validateOutput(failures, 'stdout', output, result.stdout);
583 _validateOutput(failures, 'stderr', error, result.stderr); 510 _validateOutput(failures, 'stderr', error, result.stderr);
584 511
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after
687 614
688 if (tokenEndpoint != null) { 615 if (tokenEndpoint != null) {
689 environment['_PUB_TEST_TOKEN_ENDPOINT'] = tokenEndpoint.toString(); 616 environment['_PUB_TEST_TOKEN_ENDPOINT'] = tokenEndpoint.toString();
690 } 617 }
691 618
692 return fn(dartBin, dartArgs, workingDir: pathInSandbox(appPath), 619 return fn(dartBin, dartArgs, workingDir: pathInSandbox(appPath),
693 environment: environment); 620 environment: environment);
694 }); 621 });
695 } 622 }
696 623
697 /** 624 /// Skips the current test if Git is not installed. This validates that the
698 * Skips the current test if Git is not installed. This validates that the 625 /// current test is running on a buildbot in which case we expect git to be
699 * current test is running on a buildbot in which case we expect git to be 626 /// installed. If we are not running on the buildbot, we will instead see if
700 * installed. If we are not running on the buildbot, we will instead see if git 627 /// git is installed and skip the test if not. This way, users don't need to
701 * is installed and skip the test if not. This way, users don't need to have git 628 /// have git installed to run the tests locally (unless they actually care
702 * installed to run the tests locally (unless they actually care about the pub 629 /// about the pub git tests).
703 * git tests).
704 */
705 void ensureGit() { 630 void ensureGit() {
706 _schedule((_) { 631 _schedule((_) {
707 return isGitInstalled.transform((installed) { 632 return isGitInstalled.transform((installed) {
708 if (!installed && 633 if (!installed &&
709 !Platform.environment.containsKey('BUILDBOT_BUILDERNAME')) { 634 !Platform.environment.containsKey('BUILDBOT_BUILDERNAME')) {
710 _abortScheduled = true; 635 _abortScheduled = true;
711 } 636 }
712 return null; 637 return null;
713 }); 638 });
714 }); 639 });
(...skipping 28 matching lines...) Expand all
743 if (future != null) { 668 if (future != null) {
744 return future.chain(runNextEvent); 669 return future.chain(runNextEvent);
745 } else { 670 } else {
746 return runNextEvent(null); 671 return runNextEvent(null);
747 } 672 }
748 } 673 }
749 674
750 return runNextEvent(null); 675 return runNextEvent(null);
751 } 676 }
752 677
753 /** 678 /// Compares the [actual] output from running pub with [expected]. For [String]
754 * Compares the [actual] output from running pub with [expected]. For [String] 679 /// patterns, ignores leading and trailing whitespace differences and tries to
755 * patterns, ignores leading and trailing whitespace differences and tries to 680 /// report the offending difference in a nice way. For other [Pattern]s, just
756 * report the offending difference in a nice way. For other [Pattern]s, just 681 /// reports whether the output contained the pattern.
757 * reports whether the output contained the pattern.
758 */
759 void _validateOutput(List<String> failures, String pipe, Pattern expected, 682 void _validateOutput(List<String> failures, String pipe, Pattern expected,
760 List<String> actual) { 683 List<String> actual) {
761 if (expected == null) return; 684 if (expected == null) return;
762 685
763 if (expected is RegExp) { 686 if (expected is RegExp) {
764 _validateOutputRegex(failures, pipe, expected, actual); 687 _validateOutputRegex(failures, pipe, expected, actual);
765 } else { 688 } else {
766 _validateOutputString(failures, pipe, expected, actual); 689 _validateOutputString(failures, pipe, expected, actual);
767 } 690 }
768 } 691 }
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
820 743
821 // If any lines mismatched, show the expected and actual. 744 // If any lines mismatched, show the expected and actual.
822 if (failed) { 745 if (failed) {
823 failures.add('Expected $pipe:'); 746 failures.add('Expected $pipe:');
824 failures.addAll(expected.map((line) => '| $line')); 747 failures.addAll(expected.map((line) => '| $line'));
825 failures.add('Got:'); 748 failures.add('Got:');
826 failures.addAll(results); 749 failures.addAll(results);
827 } 750 }
828 } 751 }
829 752
830 /** 753 /// Base class for [FileDescriptor] and [DirectoryDescriptor] so that a
831 * Base class for [FileDescriptor] and [DirectoryDescriptor] so that a 754 /// directory can contain a heterogeneous collection of files and
832 * directory can contain a heterogeneous collection of files and 755 /// subdirectories.
833 * subdirectories.
834 */
835 abstract class Descriptor { 756 abstract class Descriptor {
836 /** 757 /// The name of this file or directory. This must be a [String] if the file
837 * The name of this file or directory. This must be a [String] if the fiel or 758 /// or directory is going to be created.
838 * directory is going to be created.
839 */
840 final Pattern name; 759 final Pattern name;
841 760
842 Descriptor(this.name); 761 Descriptor(this.name);
843 762
844 /** 763 /// Creates the file or directory within [dir]. Returns a [Future] that is
845 * Creates the file or directory within [dir]. Returns a [Future] that is 764 /// completed after the creation is done.
846 * completed after the creation is done.
847 */
848 Future create(dir); 765 Future create(dir);
849 766
850 /** 767 /// Validates that this descriptor correctly matches the corresponding file
851 * Validates that this descriptor correctly matches the corresponding file 768 /// system entry within [dir]. Returns a [Future] that completes to `null` if
852 * system entry within [dir]. Returns a [Future] that completes to `null` if 769 /// the entry is valid, or throws an error if it failed.
853 * the entry is valid, or throws an error if it failed.
854 */
855 Future validate(String dir); 770 Future validate(String dir);
856 771
857 /** 772 /// Deletes the file or directory within [dir]. Returns a [Future] that is
858 * Deletes the file or directory within [dir]. Returns a [Future] that is 773 /// completed after the deletion is done.
859 * completed after the deletion is done.
860 */
861 Future delete(String dir); 774 Future delete(String dir);
862 775
863 /** 776 /// Loads the file at [path] from within this descriptor. If [path] is empty,
864 * Loads the file at [path] from within this descriptor. If [path] is empty, 777 /// loads the contents of the descriptor itself.
865 * loads the contents of the descriptor itself.
866 */
867 InputStream load(List<String> path); 778 InputStream load(List<String> path);
868 779
869 /** 780 /// Schedules the directory to be created before Pub is run with [runPub].
870 * Schedules the directory to be created before Pub is run with [runPub]. The 781 /// The directory will be created relative to the sandbox directory.
871 * directory will be created relative to the sandbox directory.
872 */
873 // TODO(nweiz): Use implicit closurization once issue 2984 is fixed. 782 // TODO(nweiz): Use implicit closurization once issue 2984 is fixed.
874 void scheduleCreate() => _schedule((dir) => this.create(dir)); 783 void scheduleCreate() => _schedule((dir) => this.create(dir));
875 784
876 /** 785 /// Schedules the file or directory to be deleted recursively.
877 * Schedules the file or directory to be deleted recursively.
878 */
879 void scheduleDelete() => _schedule((dir) => this.delete(dir)); 786 void scheduleDelete() => _schedule((dir) => this.delete(dir));
880 787
881 /** 788 /// Schedules the directory to be validated after Pub is run with [runPub].
882 * Schedules the directory to be validated after Pub is run with [runPub]. The 789 /// The directory will be validated relative to the sandbox directory.
883 * directory will be validated relative to the sandbox directory.
884 */
885 void scheduleValidate() => _schedule((parentDir) => validate(parentDir.path)); 790 void scheduleValidate() => _schedule((parentDir) => validate(parentDir.path));
886 791
887 /** 792 /// Asserts that the name of the descriptor is a [String] and returns it.
888 * Asserts that the name of the descriptor is a [String] and returns it.
889 */
890 String get _stringName { 793 String get _stringName {
891 if (name is String) return name; 794 if (name is String) return name;
892 throw 'Pattern $name must be a string.'; 795 throw 'Pattern $name must be a string.';
893 } 796 }
894 797
895 /** 798 /// Validates that at least one file in [dir] matching [name] is valid
896 * Validates that at least one file in [dir] matching [name] is valid 799 /// according to [validate]. [validate] should complete to an exception if
897 * according to [validate]. [validate] should complete to an exception if the 800 /// the input path is invalid.
898 * input path is invalid.
899 */
900 Future _validateOneMatch(String dir, Future validate(String path)) { 801 Future _validateOneMatch(String dir, Future validate(String path)) {
901 // Special-case strings to support multi-level names like "myapp/packages". 802 // Special-case strings to support multi-level names like "myapp/packages".
902 if (name is String) { 803 if (name is String) {
903 var path = join(dir, name); 804 var path = join(dir, name);
904 return exists(path).chain((exists) { 805 return exists(path).chain((exists) {
905 if (!exists) Expect.fail('File $name in $dir not found.'); 806 if (!exists) Expect.fail('File $name in $dir not found.');
906 return validate(path); 807 return validate(path);
907 }); 808 });
908 } 809 }
909 810
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
953 future.then((_) { 854 future.then((_) {
954 successes++; 855 successes++;
955 checkComplete(); 856 checkComplete();
956 }); 857 });
957 } 858 }
958 return completer.future; 859 return completer.future;
959 }); 860 });
960 } 861 }
961 } 862 }
962 863
963 /** 864 /// Describes a file. These are used both for setting up an expected directory
964 * Describes a file. These are used both for setting up an expected directory 865 /// tree before running a test, and for validating that the file system matches
965 * tree before running a test, and for validating that the file system matches 866 /// some expectations after running it.
966 * some expectations after running it.
967 */
968 class FileDescriptor extends Descriptor { 867 class FileDescriptor extends Descriptor {
969 /** 868 /// The text contents of the file.
970 * The text contents of the file.
971 */
972 final String contents; 869 final String contents;
973 870
974 FileDescriptor(Pattern name, this.contents) : super(name); 871 FileDescriptor(Pattern name, this.contents) : super(name);
975 872
976 /** 873 /// Creates the file within [dir]. Returns a [Future] that is completed after
977 * Creates the file within [dir]. Returns a [Future] that is completed after 874 /// the creation is done.
978 * the creation is done.
979 */
980 Future<File> create(dir) { 875 Future<File> create(dir) {
981 return writeTextFile(join(dir, _stringName), contents); 876 return writeTextFile(join(dir, _stringName), contents);
982 } 877 }
983 878
984 /** 879 /// Deletes the file within [dir]. Returns a [Future] that is completed after
985 * Deletes the file within [dir]. Returns a [Future] that is completed after 880 /// the deletion is done.
986 * the deletion is done.
987 */
988 Future delete(dir) { 881 Future delete(dir) {
989 return deleteFile(join(dir, _stringName)); 882 return deleteFile(join(dir, _stringName));
990 } 883 }
991 884
992 /** 885 /// Validates that this file correctly matches the actual file at [path].
993 * Validates that this file correctly matches the actual file at [path].
994 */
995 Future validate(String path) { 886 Future validate(String path) {
996 return _validateOneMatch(path, (file) { 887 return _validateOneMatch(path, (file) {
997 return readTextFile(file).transform((text) { 888 return readTextFile(file).transform((text) {
998 if (text == contents) return null; 889 if (text == contents) return null;
999 890
1000 Expect.fail('File $file should contain:\n\n$contents\n\n' 891 Expect.fail('File $file should contain:\n\n$contents\n\n'
1001 'but contained:\n\n$text'); 892 'but contained:\n\n$text');
1002 }); 893 });
1003 }); 894 });
1004 } 895 }
1005 896
1006 /** 897 /// Loads the contents of the file.
1007 * Loads the contents of the file.
1008 */
1009 InputStream load(List<String> path) { 898 InputStream load(List<String> path) {
1010 if (!path.isEmpty) { 899 if (!path.isEmpty) {
1011 var joinedPath = Strings.join(path, '/'); 900 var joinedPath = Strings.join(path, '/');
1012 throw "Can't load $joinedPath from within $name: not a directory."; 901 throw "Can't load $joinedPath from within $name: not a directory.";
1013 } 902 }
1014 903
1015 var stream = new ListInputStream(); 904 var stream = new ListInputStream();
1016 stream.write(contents.charCodes); 905 stream.write(contents.charCodes);
1017 stream.markEndOfStream(); 906 stream.markEndOfStream();
1018 return stream; 907 return stream;
1019 } 908 }
1020 } 909 }
1021 910
1022 /** 911 /// Describes a directory and its contents. These are used both for setting up
1023 * Describes a directory and its contents. These are used both for setting up 912 /// an expected directory tree before running a test, and for validating that
1024 * an expected directory tree before running a test, and for validating that 913 /// the file system matches some expectations after running it.
1025 * the file system matches some expectations after running it.
1026 */
1027 class DirectoryDescriptor extends Descriptor { 914 class DirectoryDescriptor extends Descriptor {
1028 /** 915 /// The files and directories contained in this directory.
1029 * The files and directories contained in this directory.
1030 */
1031 final List<Descriptor> contents; 916 final List<Descriptor> contents;
1032 917
1033 DirectoryDescriptor(Pattern name, List<Descriptor> contents) 918 DirectoryDescriptor(Pattern name, List<Descriptor> contents)
1034 : this.contents = contents == null ? <Descriptor>[] : contents, 919 : this.contents = contents == null ? <Descriptor>[] : contents,
1035 super(name); 920 super(name);
1036 921
1037 /** 922 /// Creates the file within [dir]. Returns a [Future] that is completed after
1038 * Creates the file within [dir]. Returns a [Future] that is completed after 923 /// the creation is done.
1039 * the creation is done.
1040 */
1041 Future<Directory> create(parentDir) { 924 Future<Directory> create(parentDir) {
1042 // Create the directory. 925 // Create the directory.
1043 return ensureDir(join(parentDir, _stringName)).chain((dir) { 926 return ensureDir(join(parentDir, _stringName)).chain((dir) {
1044 if (contents == null) return new Future<Directory>.immediate(dir); 927 if (contents == null) return new Future<Directory>.immediate(dir);
1045 928
1046 // Recursively create all of its children. 929 // Recursively create all of its children.
1047 final childFutures = contents.map((child) => child.create(dir)); 930 final childFutures = contents.map((child) => child.create(dir));
1048 // Only complete once all of the children have been created too. 931 // Only complete once all of the children have been created too.
1049 return Futures.wait(childFutures).transform((_) => dir); 932 return Futures.wait(childFutures).transform((_) => dir);
1050 }); 933 });
1051 } 934 }
1052 935
1053 /** 936 /// Deletes the directory within [dir]. Returns a [Future] that is completed
1054 * Deletes the directory within [dir]. Returns a [Future] that is completed 937 /// after the deletion is done.
1055 * after the deletion is done.
1056 */
1057 Future delete(dir) { 938 Future delete(dir) {
1058 return deleteDir(join(dir, _stringName)); 939 return deleteDir(join(dir, _stringName));
1059 } 940 }
1060 941
1061 /** 942 /// Validates that the directory at [path] contains all of the expected
1062 * Validates that the directory at [path] contains all of the expected 943 /// contents in this descriptor. Note that this does *not* check that the
1063 * contents in this descriptor. Note that this does *not* check that the 944 /// directory doesn't contain other unexpected stuff, just that it *does*
1064 * directory doesn't contain other unexpected stuff, just that it *does* 945 /// contain the stuff we do expect.
1065 * contain the stuff we do expect.
1066 */
1067 Future validate(String path) { 946 Future validate(String path) {
1068 return _validateOneMatch(path, (dir) { 947 return _validateOneMatch(path, (dir) {
1069 // Validate each of the items in this directory. 948 // Validate each of the items in this directory.
1070 final entryFutures = contents.map((entry) => entry.validate(dir)); 949 final entryFutures = contents.map((entry) => entry.validate(dir));
1071 950
1072 // If they are all valid, the directory is valid. 951 // If they are all valid, the directory is valid.
1073 return Futures.wait(entryFutures).transform((entries) => null); 952 return Futures.wait(entryFutures).transform((entries) => null);
1074 }); 953 });
1075 } 954 }
1076 955
1077 /** 956 /// Loads [path] from within this directory.
1078 * Loads [path] from within this directory.
1079 */
1080 InputStream load(List<String> path) { 957 InputStream load(List<String> path) {
1081 if (path.isEmpty) { 958 if (path.isEmpty) {
1082 throw "Can't load the contents of $name: is a directory."; 959 throw "Can't load the contents of $name: is a directory.";
1083 } 960 }
1084 961
1085 for (var descriptor in contents) { 962 for (var descriptor in contents) {
1086 if (descriptor.name == path[0]) { 963 if (descriptor.name == path[0]) {
1087 return descriptor.load(path.getRange(1, path.length - 1)); 964 return descriptor.load(path.getRange(1, path.length - 1));
1088 } 965 }
1089 } 966 }
1090 967
1091 throw "Directory $name doesn't contain ${Strings.join(path, '/')}."; 968 throw "Directory $name doesn't contain ${Strings.join(path, '/')}.";
1092 } 969 }
1093 } 970 }
1094 971
1095 /** 972 /// Wraps a [Future] that will complete to a [Descriptor] and makes it behave
1096 * Wraps a [Future] that will complete to a [Descriptor] and makes it behave 973 /// like a concrete [Descriptor]. This is necessary when the contents of the
1097 * like a concrete [Descriptor]. This is necessary when the contents of the 974 /// descriptor depends on information that's not available until part of the
1098 * descriptor depends on information that's not available until part of the test 975 /// test run is completed.
1099 * run is completed.
1100 */
1101 class FutureDescriptor extends Descriptor { 976 class FutureDescriptor extends Descriptor {
1102 Future<Descriptor> _future; 977 Future<Descriptor> _future;
1103 978
1104 FutureDescriptor(this._future) : super('<unknown>'); 979 FutureDescriptor(this._future) : super('<unknown>');
1105 980
1106 Future create(dir) => _future.chain((desc) => desc.create(dir)); 981 Future create(dir) => _future.chain((desc) => desc.create(dir));
1107 982
1108 Future validate(dir) => _future.chain((desc) => desc.validate(dir)); 983 Future validate(dir) => _future.chain((desc) => desc.validate(dir));
1109 984
1110 Future delete(dir) => _future.chain((desc) => desc.delete(dir)); 985 Future delete(dir) => _future.chain((desc) => desc.delete(dir));
1111 986
1112 InputStream load(List<String> path) { 987 InputStream load(List<String> path) {
1113 var resultStream = new ListInputStream(); 988 var resultStream = new ListInputStream();
1114 _future.then((desc) => pipeInputToInput(desc.load(path), resultStream)); 989 _future.then((desc) => pipeInputToInput(desc.load(path), resultStream));
1115 return resultStream; 990 return resultStream;
1116 } 991 }
1117 } 992 }
1118 993
1119 /** 994 /// Describes a Git repository and its contents.
1120 * Describes a Git repository and its contents.
1121 */
1122 class GitRepoDescriptor extends DirectoryDescriptor { 995 class GitRepoDescriptor extends DirectoryDescriptor {
1123 GitRepoDescriptor(Pattern name, List<Descriptor> contents) 996 GitRepoDescriptor(Pattern name, List<Descriptor> contents)
1124 : super(name, contents); 997 : super(name, contents);
1125 998
1126 /** 999 /// Creates the Git repository and commits the contents.
1127 * Creates the Git repository and commits the contents.
1128 */
1129 Future<Directory> create(parentDir) { 1000 Future<Directory> create(parentDir) {
1130 return _runGitCommands(parentDir, [ 1001 return _runGitCommands(parentDir, [
1131 ['init'], 1002 ['init'],
1132 ['add', '.'], 1003 ['add', '.'],
1133 ['commit', '-m', 'initial commit'] 1004 ['commit', '-m', 'initial commit']
1134 ]); 1005 ]);
1135 } 1006 }
1136 1007
1137 /** 1008 /// Commits any changes to the Git repository.
1138 * Commits any changes to the Git repository.
1139 */
1140 Future commit(parentDir) { 1009 Future commit(parentDir) {
1141 return _runGitCommands(parentDir, [ 1010 return _runGitCommands(parentDir, [
1142 ['add', '.'], 1011 ['add', '.'],
1143 ['commit', '-m', 'update'] 1012 ['commit', '-m', 'update']
1144 ]); 1013 ]);
1145 } 1014 }
1146 1015
1147 /** 1016 /// Schedules changes to be committed to the Git repository.
1148 * Schedules changes to be committed to the Git repository.
1149 */
1150 void scheduleCommit() => _schedule((dir) => this.commit(dir)); 1017 void scheduleCommit() => _schedule((dir) => this.commit(dir));
1151 1018
1152 /** 1019 /// Return a Future that completes to the commit in the git repository
1153 * Return a Future that completes to the commit in the git repository referred 1020 /// referred to by [ref] at the current point in the scheduled test run.
1154 * to by [ref] at the current point in the scheduled test run.
1155 */
1156 Future<String> revParse(String ref) { 1021 Future<String> revParse(String ref) {
1157 return _scheduleValue((parentDir) { 1022 return _scheduleValue((parentDir) {
1158 return super.create(parentDir).chain((rootDir) { 1023 return super.create(parentDir).chain((rootDir) {
1159 return _runGit(['rev-parse', ref], rootDir); 1024 return _runGit(['rev-parse', ref], rootDir);
1160 }).transform((output) => output[0]); 1025 }).transform((output) => output[0]);
1161 }); 1026 });
1162 } 1027 }
1163 1028
1164 /// Schedule a Git command to run in this repository. 1029 /// Schedule a Git command to run in this repository.
1165 void scheduleGit(List<String> args) { 1030 void scheduleGit(List<String> args) {
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
1199 if (!result.success) { 1064 if (!result.success) {
1200 throw "Error running: git ${Strings.join(args, ' ')}\n" 1065 throw "Error running: git ${Strings.join(args, ' ')}\n"
1201 "${Strings.join(result.stderr, '\n')}"; 1066 "${Strings.join(result.stderr, '\n')}";
1202 } 1067 }
1203 1068
1204 return result.stdout; 1069 return result.stdout;
1205 }); 1070 });
1206 } 1071 }
1207 } 1072 }
1208 1073
1209 /** 1074 /// Describes a gzipped tar file and its contents.
1210 * Describes a gzipped tar file and its contents.
1211 */
1212 class TarFileDescriptor extends Descriptor { 1075 class TarFileDescriptor extends Descriptor {
1213 final List<Descriptor> contents; 1076 final List<Descriptor> contents;
1214 1077
1215 TarFileDescriptor(Pattern name, this.contents) 1078 TarFileDescriptor(Pattern name, this.contents)
1216 : super(name); 1079 : super(name);
1217 1080
1218 /** 1081 /// Creates the files and directories within this tar file, then archives
1219 * Creates the files and directories within this tar file, then archives them, 1082 /// them, compresses them, and saves the result to [parentDir].
1220 * compresses them, and saves the result to [parentDir].
1221 */
1222 Future<File> create(parentDir) { 1083 Future<File> create(parentDir) {
1223 // TODO(rnystrom): Use withTempDir(). 1084 // TODO(rnystrom): Use withTempDir().
1224 var tempDir; 1085 var tempDir;
1225 return createTempDir().chain((_tempDir) { 1086 return createTempDir().chain((_tempDir) {
1226 tempDir = _tempDir; 1087 tempDir = _tempDir;
1227 return Futures.wait(contents.map((child) => child.create(tempDir))); 1088 return Futures.wait(contents.map((child) => child.create(tempDir)));
1228 }).chain((createdContents) { 1089 }).chain((createdContents) {
1229 return consumeInputStream(createTarGz(createdContents, baseDir: tempDir)); 1090 return consumeInputStream(createTarGz(createdContents, baseDir: tempDir));
1230 }).chain((bytes) { 1091 }).chain((bytes) {
1231 return new File(join(parentDir, _stringName)).writeAsBytes(bytes); 1092 return new File(join(parentDir, _stringName)).writeAsBytes(bytes);
1232 }).chain((file) { 1093 }).chain((file) {
1233 return deleteDir(tempDir).transform((_) => file); 1094 return deleteDir(tempDir).transform((_) => file);
1234 }); 1095 });
1235 } 1096 }
1236 1097
1237 /** 1098 /// Validates that the `.tar.gz` file at [path] contains the expected
1238 * Validates that the `.tar.gz` file at [path] contains the expected contents. 1099 /// contents.
1239 */
1240 Future validate(String path) { 1100 Future validate(String path) {
1241 throw "TODO(nweiz): implement this"; 1101 throw "TODO(nweiz): implement this";
1242 } 1102 }
1243 1103
1244 Future delete(dir) { 1104 Future delete(dir) {
1245 throw new UnsupportedError(''); 1105 throw new UnsupportedError('');
1246 } 1106 }
1247 1107
1248 /** 1108 /// Loads the contents of this tar file.
1249 * Loads the contents of this tar file.
1250 */
1251 InputStream load(List<String> path) { 1109 InputStream load(List<String> path) {
1252 if (!path.isEmpty) { 1110 if (!path.isEmpty) {
1253 var joinedPath = Strings.join(path, '/'); 1111 var joinedPath = Strings.join(path, '/');
1254 throw "Can't load $joinedPath from within $name: not a directory."; 1112 throw "Can't load $joinedPath from within $name: not a directory.";
1255 } 1113 }
1256 1114
1257 var sinkStream = new ListInputStream(); 1115 var sinkStream = new ListInputStream();
1258 var tempDir; 1116 var tempDir;
1259 // TODO(rnystrom): Use withTempDir() here. 1117 // TODO(rnystrom): Use withTempDir() here.
1260 // TODO(nweiz): propagate any errors to the return value. See issue 3657. 1118 // TODO(nweiz): propagate any errors to the return value. See issue 3657.
1261 createTempDir().chain((_tempDir) { 1119 createTempDir().chain((_tempDir) {
1262 tempDir = _tempDir; 1120 tempDir = _tempDir;
1263 return create(tempDir); 1121 return create(tempDir);
1264 }).then((tar) { 1122 }).then((tar) {
1265 var sourceStream = tar.openInputStream(); 1123 var sourceStream = tar.openInputStream();
1266 pipeInputToInput(sourceStream, sinkStream).then((_) { 1124 pipeInputToInput(sourceStream, sinkStream).then((_) {
1267 tempDir.delete(recursive: true); 1125 tempDir.delete(recursive: true);
1268 }); 1126 });
1269 }); 1127 });
1270 return sinkStream; 1128 return sinkStream;
1271 } 1129 }
1272 } 1130 }
1273 1131
1274 /** 1132 /// A descriptor that validates that no file exists with the given name.
1275 * A descriptor that validates that no file exists with the given name.
1276 */
1277 class NothingDescriptor extends Descriptor { 1133 class NothingDescriptor extends Descriptor {
1278 NothingDescriptor(String name) : super(name); 1134 NothingDescriptor(String name) : super(name);
1279 1135
1280 Future create(dir) => new Future.immediate(null); 1136 Future create(dir) => new Future.immediate(null);
1281 Future delete(dir) => new Future.immediate(null); 1137 Future delete(dir) => new Future.immediate(null);
1282 1138
1283 Future validate(String dir) { 1139 Future validate(String dir) {
1284 return exists(join(dir, name)).transform((exists) { 1140 return exists(join(dir, name)).transform((exists) {
1285 if (exists) Expect.fail('File $name in $dir should not exist.'); 1141 if (exists) Expect.fail('File $name in $dir should not exist.');
1286 }); 1142 });
(...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after
1532 return; 1388 return;
1533 }); 1389 });
1534 } 1390 }
1535 1391
1536 return printStream('stdout', _stdout.value) 1392 return printStream('stdout', _stdout.value)
1537 .chain((_) => printStream('stderr', _stderr.value)); 1393 .chain((_) => printStream('stderr', _stderr.value));
1538 } 1394 }
1539 } 1395 }
1540 1396
1541 /// A class representing an [HttpServer] that's scheduled to run in the course 1397 /// A class representing an [HttpServer] that's scheduled to run in the course
1542 /// of the test. This class allows the server's request handling to be scheduled 1398 /// of the test. This class allows the server's request handling to be
1543 /// synchronously. All operations on this class are scheduled. 1399 /// scheduled synchronously. All operations on this class are scheduled.
1544 class ScheduledServer { 1400 class ScheduledServer {
1545 /// The wrapped server. 1401 /// The wrapped server.
1546 final Future<HttpServer> _server; 1402 final Future<HttpServer> _server;
1547 1403
1548 /// The queue of handlers to run for upcoming requests. 1404 /// The queue of handlers to run for upcoming requests.
1549 final _handlers = new Queue<Future>(); 1405 final _handlers = new Queue<Future>();
1550 1406
1551 /// The requests to be ignored. 1407 /// The requests to be ignored.
1552 final _ignored = new Set<Pair<String, String>>(); 1408 final _ignored = new Set<Pair<String, String>>();
1553 1409
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
1611 } 1467 }
1612 return _handlers.removeFirst(); 1468 return _handlers.removeFirst();
1613 }).transform((handler) { 1469 }).transform((handler) {
1614 handler(request, response); 1470 handler(request, response);
1615 }), _SCHEDULE_TIMEOUT, "waiting for a handler for ${request.method} " 1471 }), _SCHEDULE_TIMEOUT, "waiting for a handler for ${request.method} "
1616 "${request.path}"); 1472 "${request.path}");
1617 expect(future, completes); 1473 expect(future, completes);
1618 } 1474 }
1619 } 1475 }
1620 1476
1621 /** 1477 /// Takes a simple data structure (composed of [Map]s, [List]s, scalar objects,
1622 * Takes a simple data structure (composed of [Map]s, [List]s, scalar objects, 1478 /// and [Future]s) and recursively resolves all the [Future]s contained within.
1623 * and [Future]s) and recursively resolves all the [Future]s contained within. 1479 /// Completes with the fully resolved structure.
1624 * Completes with the fully resolved structure.
1625 */
1626 Future _awaitObject(object) { 1480 Future _awaitObject(object) {
1627 // Unroll nested futures. 1481 // Unroll nested futures.
1628 if (object is Future) return object.chain(_awaitObject); 1482 if (object is Future) return object.chain(_awaitObject);
1629 if (object is Collection) return Futures.wait(object.map(_awaitObject)); 1483 if (object is Collection) return Futures.wait(object.map(_awaitObject));
1630 if (object is! Map) return new Future.immediate(object); 1484 if (object is! Map) return new Future.immediate(object);
1631 1485
1632 var pairs = <Future<Pair>>[]; 1486 var pairs = <Future<Pair>>[];
1633 object.forEach((key, value) { 1487 object.forEach((key, value) {
1634 pairs.add(_awaitObject(value) 1488 pairs.add(_awaitObject(value)
1635 .transform((resolved) => new Pair(key, resolved))); 1489 .transform((resolved) => new Pair(key, resolved)));
1636 }); 1490 });
1637 return Futures.wait(pairs).transform((resolvedPairs) { 1491 return Futures.wait(pairs).transform((resolvedPairs) {
1638 var map = {}; 1492 var map = {};
1639 for (var pair in resolvedPairs) { 1493 for (var pair in resolvedPairs) {
1640 map[pair.first] = pair.last; 1494 map[pair.first] = pair.last;
1641 } 1495 }
1642 return map; 1496 return map;
1643 }); 1497 });
1644 } 1498 }
1645 1499
1646 /** 1500 /// Schedules a callback to be called as part of the test case.
1647 * Schedules a callback to be called as part of the test case.
1648 */
1649 void _schedule(_ScheduledEvent event) { 1501 void _schedule(_ScheduledEvent event) {
1650 if (_scheduled == null) _scheduled = []; 1502 if (_scheduled == null) _scheduled = [];
1651 _scheduled.add(event); 1503 _scheduled.add(event);
1652 } 1504 }
1653 1505
1654 /// Like [_schedule], but pipes the return value of [event] to a returned 1506 /// Like [_schedule], but pipes the return value of [event] to a returned
1655 /// [Future]. 1507 /// [Future].
1656 Future _scheduleValue(_ScheduledEvent event) { 1508 Future _scheduleValue(_ScheduledEvent event) {
1657 var completer = new Completer(); 1509 var completer = new Completer();
1658 _schedule((parentDir) { 1510 _schedule((parentDir) {
1659 chainToCompleter(event(parentDir), completer); 1511 chainToCompleter(event(parentDir), completer);
1660 return completer.future; 1512 return completer.future;
1661 }); 1513 });
1662 return completer.future; 1514 return completer.future;
1663 } 1515 }
1664 1516
1665 /// Schedules a callback to be called after the test case has completed, even if 1517 /// Schedules a callback to be called after the test case has completed, even
1666 /// it failed. 1518 /// if it failed.
1667 void _scheduleCleanup(_ScheduledEvent event) { 1519 void _scheduleCleanup(_ScheduledEvent event) {
1668 if (_scheduledCleanup == null) _scheduledCleanup = []; 1520 if (_scheduledCleanup == null) _scheduledCleanup = [];
1669 _scheduledCleanup.add(event); 1521 _scheduledCleanup.add(event);
1670 } 1522 }
1671 1523
1672 /// Schedules a callback to be called after the test case has completed, but 1524 /// Schedules a callback to be called after the test case has completed, but
1673 /// only if it failed. 1525 /// only if it failed.
1674 void _scheduleOnException(_ScheduledEvent event) { 1526 void _scheduleOnException(_ScheduledEvent event) {
1675 if (_scheduledOnException == null) _scheduledOnException = []; 1527 if (_scheduledOnException == null) _scheduledOnException = [];
1676 _scheduledOnException.add(event); 1528 _scheduledOnException.add(event);
1677 } 1529 }
1678 1530
1679 /// Like [expect], but for [Future]s that complete as part of the scheduled 1531 /// Like [expect], but for [Future]s that complete as part of the scheduled
1680 /// test. This is necessary to ensure that the exception thrown by the 1532 /// test. This is necessary to ensure that the exception thrown by the
1681 /// expectation failing is handled by the scheduler. 1533 /// expectation failing is handled by the scheduler.
1682 /// 1534 ///
1683 /// Note that [matcher] matches against the completed value of [actual], so 1535 /// Note that [matcher] matches against the completed value of [actual], so
1684 /// calling [completion] is unnecessary. 1536 /// calling [completion] is unnecessary.
1685 void expectLater(Future actual, matcher, {String reason, 1537 void expectLater(Future actual, matcher, {String reason,
1686 FailureHandler failureHandler, bool verbose: false}) { 1538 FailureHandler failureHandler, bool verbose: false}) {
1687 _schedule((_) { 1539 _schedule((_) {
1688 return actual.transform((value) { 1540 return actual.transform((value) {
1689 expect(value, matcher, reason: reason, failureHandler: failureHandler, 1541 expect(value, matcher, reason: reason, failureHandler: failureHandler,
1690 verbose: false); 1542 verbose: false);
1691 }); 1543 });
1692 }); 1544 });
1693 } 1545 }
OLDNEW
« no previous file with comments | « no previous file | utils/tests/pub/version_solver_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698