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

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

Issue 10699068: Add numerous features to the Pub test infrastructure. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 5 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 | « utils/tests/pub/pub_test.dart ('k') | no next file » | 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 /**
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 */
11 #library('test_pub'); 11 #library('test_pub');
12 12
13 #import('dart:io'); 13 #import('dart:io');
14 #import('dart:uri'); 14 #import('dart:uri');
15 15
16 #import('../../../lib/unittest/unittest.dart'); 16 #import('../../../lib/unittest/unittest.dart');
17 #import('../../lib/file_system.dart', prefix: 'fs'); 17 #import('../../lib/file_system.dart', prefix: 'fs');
18 #import('../../pub/io.dart'); 18 #import('../../pub/io.dart');
19 #import('../../pub/utils.dart');
19 #import('../../pub/yaml/yaml.dart'); 20 #import('../../pub/yaml/yaml.dart');
20 21
21 /** 22 /**
22 * Creates a new [FileDescriptor] with [name] and [contents]. 23 * Creates a new [FileDescriptor] with [name] and [contents].
23 */ 24 */
24 FileDescriptor file(String name, String contents) => 25 FileDescriptor file(Pattern name, String contents) =>
25 new FileDescriptor(name, contents); 26 new FileDescriptor(name, contents);
26 27
27 /** 28 /**
28 * Creates a new [DirectoryDescriptor] with [name] and [contents]. 29 * Creates a new [DirectoryDescriptor] with [name] and [contents].
29 */ 30 */
30 DirectoryDescriptor dir(String name, [List<Descriptor> contents]) => 31 DirectoryDescriptor dir(Pattern name, [List<Descriptor> contents]) =>
31 new DirectoryDescriptor(name, contents); 32 new DirectoryDescriptor(name, contents);
32 33
33 /** 34 /**
34 * Creates a new [GitRepoDescriptor] with [name] and [contents]. 35 * Creates a new [GitRepoDescriptor] with [name] and [contents].
35 */ 36 */
36 DirectoryDescriptor git(String name, [List<Descriptor> contents]) => 37 DirectoryDescriptor git(Pattern name, [List<Descriptor> contents]) =>
37 new GitRepoDescriptor(name, contents); 38 new GitRepoDescriptor(name, contents);
38 39
39 /** 40 /**
40 * Creates a new [TarFileDescriptor] with [name] and [contents]. 41 * Creates a new [TarFileDescriptor] with [name] and [contents].
41 */ 42 */
42 TarFileDescriptor tar(String name, [List<Descriptor> contents]) => 43 TarFileDescriptor tar(Pattern name, [List<Descriptor> contents]) =>
43 new TarFileDescriptor(name, contents); 44 new TarFileDescriptor(name, contents);
44 45
45 /** 46 /**
46 * Creates an HTTP server to serve [contents] as static files. This server will 47 * Creates an HTTP server to serve [contents] as static files. This server will
47 * exist only for the duration of the pub run. 48 * exist only for the duration of the pub run.
48 */ 49 */
49 void serve(String host, int port, [List<Descriptor> contents]) { 50 void serve(String host, int port, [List<Descriptor> contents]) {
50 var baseDir = dir("serve-dir", contents); 51 var baseDir = dir("serve-dir", contents);
51 if (host == 'localhost') { 52 if (host == 'localhost') {
52 host = '127.0.0.1'; 53 host = '127.0.0.1';
53 } 54 }
54 55
55 _scheduleBeforePub((_) { 56 _schedule((_) {
56 var server = new HttpServer(); 57 var server = new HttpServer();
57 server.defaultRequestHandler = (request, response) { 58 server.defaultRequestHandler = (request, response) {
58 var path = request.uri.replaceFirst("/", "").split("/"); 59 var path = request.uri.replaceFirst("/", "").split("/");
59 var stream = baseDir.load(path); 60 var stream = baseDir.load(path);
60 response.persistentConnection = false; 61 response.persistentConnection = false;
61 if (stream == null) { 62 if (stream == null) {
62 response.statusCode = 404; 63 response.statusCode = 404;
63 response.outputStream.close(); 64 response.outputStream.close();
64 return; 65 return;
65 } 66 }
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
138 */ 139 */
139 final String packagesPath = "$appPath/packages"; 140 final String packagesPath = "$appPath/packages";
140 141
141 /** 142 /**
142 * The type for callbacks that will be fired during [runPub]. Takes the sandbox 143 * The type for callbacks that will be fired during [runPub]. Takes the sandbox
143 * directory as a parameter. 144 * directory as a parameter.
144 */ 145 */
145 typedef Future _ScheduledEvent(Directory parentDir); 146 typedef Future _ScheduledEvent(Directory parentDir);
146 147
147 /** 148 /**
148 * The list of events that are scheduled to run after the sandbox directory has 149 * The list of events that are scheduled to run as part of the test case.
149 * been created but before Pub is run.
150 */ 150 */
151 List<_ScheduledEvent> _scheduledBeforePub; 151 List<_ScheduledEvent> _scheduled;
152 152
153 /** 153 /**
154 * The list of events that are scheduled to run after Pub has been run. 154 * The list of events that are scheduled to run after the test case, even if it
155 */ 155 * failed.
156 List<_ScheduledEvent> _scheduledAfterPub;
157
158 /**
159 * The list of events that are scheduled to run after Pub has been run, even if
160 * it failed.
161 */ 156 */
162 List<_ScheduledEvent> _scheduledCleanup; 157 List<_ScheduledEvent> _scheduledCleanup;
163 158
164 void runPub([List<String> args, Pattern output, Pattern error, 159 /**
165 int exitCode = 0]) { 160 * Runs all the scheduled events for a test case. This should only be called
161 * once per test case.
162 */
163 void run() {
166 var createdSandboxDir; 164 var createdSandboxDir;
167 165
168 var asyncDone = expectAsync0(() {}); 166 var asyncDone = expectAsync0(() {});
169 167
170 Future cleanup() { 168 Future cleanup() {
171 return _runScheduled(createdSandboxDir, _scheduledCleanup).chain((_) { 169 return _runScheduled(createdSandboxDir, _scheduledCleanup).chain((_) {
172 _scheduledBeforePub = null; 170 _scheduled = null;
173 _scheduledAfterPub = null;
174 if (createdSandboxDir != null) return deleteDir(createdSandboxDir); 171 if (createdSandboxDir != null) return deleteDir(createdSandboxDir);
175 return new Future.immediate(null); 172 return new Future.immediate(null);
176 }); 173 });
177 } 174 }
178 175
179 String pathInSandbox(path) => join(getFullPath(createdSandboxDir), path);
180
181 final future = _setUpSandbox().chain((sandboxDir) { 176 final future = _setUpSandbox().chain((sandboxDir) {
182 createdSandboxDir = sandboxDir; 177 createdSandboxDir = sandboxDir;
183 return _runScheduled(sandboxDir, _scheduledBeforePub); 178 return _runScheduled(sandboxDir, _scheduled);
184 }).chain((_) {
185 return ensureDir(pathInSandbox(appPath));
186 }).chain((_) {
187 // TODO(rnystrom): Hack in the cache directory path. Should pass this
188 // in using environment var once #752 is done.
189 args.add('--cachedir=${pathInSandbox(cachePath)}');
190
191 // TODO(rnystrom): Hack in the SDK path. Should pass this in using
192 // environment var once #752 is done.
193 args.add('--sdkdir=${pathInSandbox(sdkPath)}');
194
195 return _runPub(args, pathInSandbox(appPath), pipeStdout: output == null,
196 pipeStderr: error == null);
197 }).chain((result) {
198 _validateOutput(output, result.stdout);
199 _validateOutput(error, result.stderr);
200
201 Expect.equals(result.exitCode, exitCode,
202 'Pub returned exit code ${result.exitCode}, expected $exitCode.');
203
204 return _runScheduled(createdSandboxDir, _scheduledAfterPub);
205 }); 179 });
206 180
207 future.chain((_) => cleanup()).then((_) => asyncDone());
208
209 future.handleException((error) { 181 future.handleException((error) {
210 // If an error occurs during testing, delete the sandbox, throw the error so 182 // If an error occurs during testing, delete the sandbox, throw the error so
211 // that the test framework sees it, then finally call asyncDone so that the 183 // that the test framework sees it, then finally call asyncDone so that the
212 // test framework knows we're done doing asynchronous stuff. 184 // test framework knows we're done doing asynchronous stuff.
213 cleanup().then((_) { 185 cleanup().then((_) {
214 guardAsync(() { throw error; }, asyncDone); 186 guardAsync(() { throw error; }, asyncDone);
215 }); 187 });
216 return true; 188 return true;
217 }); 189 });
190
191 future.chain((_) => cleanup()).then((_) => asyncDone());
218 } 192 }
219 193
194 /**
195 * Schedules a call to the Pub command-line utility. Runs Pub with [args] and
196 * validates that its results match [output], [error], and [exitCode].
197 */
198 void schedulePub([List<String> args, Pattern output, Pattern error,
199 int exitCode = 0]) {
200 _schedule((sandboxDir) {
201 String pathInSandbox(path) => join(getFullPath(sandboxDir), path);
202
203 return ensureDir(pathInSandbox(appPath)).chain((_) {
204 // TODO(rnystrom): Hack in the cache directory path. Should pass this in
205 // using environment var once #752 is done.
206 args.add('--cachedir=${pathInSandbox(cachePath)}');
207
208 // TODO(rnystrom): Hack in the SDK path. Should pass this in using
209 // environment var once #752 is done.
210 args.add('--sdkdir=${pathInSandbox(sdkPath)}');
211
212 return _runPub(args, pathInSandbox(appPath), pipeStdout: output == null,
213 pipeStderr: error == null);
214 }).transform((result) {
215 _validateOutput(output, result.stdout);
216 _validateOutput(error, result.stderr);
217
218 Expect.equals(result.exitCode, exitCode,
219 'Pub returned exit code ${result.exitCode}, expected $exitCode.');
220
221 return null;
222 });
223 });
224 }
225
226 /**
227 * A shorthand for [schedulePub] and [run] when no validation needs to be done
228 * after Pub has been run.
229 */
230 void runPub([List<String> args, Pattern output, Pattern error,
231 int exitCode = 0]) {
232 schedulePub(args, output, error, exitCode);
233 run();
234 }
220 235
221 /** 236 /**
222 * Wraps a test that needs git in order to run. This validates that the test is 237 * Wraps a test that needs git in order to run. This validates that the test is
223 * running on a builbot in which case we expect git to be installed. If we are 238 * running on a builbot in which case we expect git to be installed. If we are
224 * not running on the buildbot, we will instead see if git is installed and 239 * not running on the buildbot, we will instead see if git is installed and
225 * skip the test if not. This way, users don't need to have git installed to 240 * skip the test if not. This way, users don't need to have git installed to
226 * run the tests locally (unless they actually care about the pub git tests). 241 * run the tests locally (unless they actually care about the pub git tests).
227 */ 242 */
228 void withGit(void callback()) { 243 void withGit(void callback()) {
229 isGitInstalled.then(expectAsync1((installed) { 244 isGitInstalled.then(expectAsync1((installed) {
230 if (installed || Platform.environment.containsKey('BUILDBOT_BUILDERNAME')) { 245 if (installed || Platform.environment.containsKey('BUILDBOT_BUILDERNAME')) {
231 callback(); 246 callback();
232 } 247 }
233 })); 248 }));
234 } 249 }
235 250
236 Future<Directory> _setUpSandbox() { 251 Future<Directory> _setUpSandbox() {
237 return createTempDir('pub-test-sandbox-'); 252 return createTempDir('pub-test-sandbox-');
238 } 253 }
239 254
240 _runScheduled(Directory parentDir, List<_ScheduledEvent> scheduled) { 255 Future _runScheduled(Directory parentDir, List<_ScheduledEvent> scheduled) {
241 if (scheduled == null) return new Future.immediate(null); 256 if (scheduled == null) return new Future.immediate(null);
242 var future = Futures.wait(scheduled.map((event) { 257 var iterator = scheduled.iterator();
243 var subFuture = event(parentDir); 258
244 return subFuture == null ? new Future.immediate(null) : subFuture; 259 Future runNextEvent([_]) {
245 })); 260 if (!iterator.hasNext()) {
246 scheduled.clear(); 261 scheduled.clear();
247 return future; 262 return new Future.immediate(null);
263 }
264
265 var future = iterator.next()(parentDir);
266 if (future != null) {
267 return future.chain(runNextEvent);
268 } else {
269 return runNextEvent();
270 }
271 }
272
273 return runNextEvent();
248 } 274 }
249 275
250 Future<ProcessResult> _runPub(List<String> pubArgs, String workingDir, 276 Future<ProcessResult> _runPub(List<String> pubArgs, String workingDir,
251 [bool pipeStdout=false, bool pipeStderr=false]) { 277 [bool pipeStdout=false, bool pipeStderr=false]) {
252 // Find a dart executable we can use to run pub. Uses the one that the 278 // Find a dart executable we can use to run pub. Uses the one that the
253 // test infrastructure uses. We are not using new Options.executable here 279 // test infrastructure uses. We are not using new Options.executable here
254 // because that gets confused if you invoked Dart through a shell script. 280 // because that gets confused if you invoked Dart through a shell script.
255 final scriptDir = new File(new Options().script).directorySync().path; 281 final scriptDir = new File(new Options().script).directorySync().path;
256 final platform = Platform.operatingSystem; 282 final platform = Platform.operatingSystem;
257 final dartBin = join(scriptDir, '../../../tools/testing/bin/$platform/dart'); 283 final dartBin = join(scriptDir, '../../../tools/testing/bin/$platform/dart');
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
319 } 345 }
320 } 346 }
321 347
322 /** 348 /**
323 * Base class for [FileDescriptor] and [DirectoryDescriptor] so that a 349 * Base class for [FileDescriptor] and [DirectoryDescriptor] so that a
324 * directory can contain a heterogeneous collection of files and 350 * directory can contain a heterogeneous collection of files and
325 * subdirectories. 351 * subdirectories.
326 */ 352 */
327 class Descriptor { 353 class Descriptor {
328 /** 354 /**
329 * The short name of this file or directory. 355 * The name of this file or directory. This must be a [String] if the fiel or
356 * directory is going to be created.
330 */ 357 */
331 final String name; 358 final Pattern name;
332 359
333 Descriptor(this.name); 360 Descriptor(this.name);
334 361
335 /** 362 /**
336 * Creates the file or directory within [dir]. Returns a [Future] that is 363 * Creates the file or directory within [dir]. Returns a [Future] that is
337 * completed after the creation is done. 364 * completed after the creation is done.
338 */ 365 */
339 abstract Future create(dir); 366 abstract Future create(dir);
340 367
341 /** 368 /**
342 * Validates that this descriptor correctly matches the corresponding file 369 * Validates that this descriptor correctly matches the corresponding file
343 * system entry within [dir]. Returns a [Future] that completes to `null` if 370 * system entry within [dir]. Returns a [Future] that completes to `null` if
344 * the entry is valid, or throws an error if it failed. 371 * the entry is valid, or throws an error if it failed.
345 */ 372 */
346 abstract Future validate(String dir); 373 abstract Future validate(String dir);
347 374
348 /** 375 /**
376 * Deletes the file or directory within [dir]. Returns a [Future] that is
377 * completed after the deletion is done.
378 */
379 abstract Future delete(String dir);
380
381 /**
349 * Loads the file at [path] from within this descriptor. If [path] is empty, 382 * Loads the file at [path] from within this descriptor. If [path] is empty,
350 * loads the contents of the descriptor itself. 383 * loads the contents of the descriptor itself.
351 */ 384 */
352 abstract InputStream load(List<String> path); 385 abstract InputStream load(List<String> path);
353 386
354 /** 387 /**
355 * Schedules the directory to be created before Pub is run with [runPub]. The 388 * Schedules the directory to be created before Pub is run with [runPub]. The
356 * directory will be created relative to the sandbox directory. 389 * directory will be created relative to the sandbox directory.
357 */ 390 */
358 // TODO(nweiz): Use implicit closurization once issue 2984 is fixed. 391 // TODO(nweiz): Use implicit closurization once issue 2984 is fixed.
359 void scheduleCreate() => _scheduleBeforePub((dir) => this.create(dir)); 392 void scheduleCreate() => _schedule((dir) => this.create(dir));
393
394 /**
395 * Schedules the file or directory to be deleted recursively.
396 */
397 void scheduleDelete() => _schedule((dir) => this.delete(dir));
360 398
361 /** 399 /**
362 * Schedules the directory to be validated after Pub is run with [runPub]. The 400 * Schedules the directory to be validated after Pub is run with [runPub]. The
363 * directory will be validated relative to the sandbox directory. 401 * directory will be validated relative to the sandbox directory.
364 */ 402 */
365 void scheduleValidate() => 403 void scheduleValidate() => _schedule((parentDir) => validate(parentDir.path));
366 _scheduleAfterPub((parentDir) => validate(parentDir.path)); 404
405 /**
406 * Asserts that the name of the descriptor is a [String] and returns it.
407 */
408 String get _stringName() {
409 if (name is String) return name;
410 throw 'Pattern $name must be a string.';
411 }
412
413 /**
414 * Validates that at least one file in [dir] matching [name] is valid
415 * according to [validate]. [validate] should complete to an exception if the
416 * input path is invalid.
417 */
418 Future _validateOneMatch(String dir, Future validate(String path)) {
419 // Special-case strings to support multi-level names like "myapp/packages".
420 if (name is String) {
421 var path = join(dir, name);
422 return exists(path).chain((exists) {
423 if (!exists) Expect.fail('File $name in $dir not found.');
424 return validate(path);
425 });
426 }
427
428 return listDir(dir).chain((files) {
429 var matches = files.filter((file) => endsWithPattern(file, name));
430 if (matches.length == 0) {
431 Expect.fail('No files in $dir match pattern $name.');
432 }
433 if (matches.length == 1) return validate(matches[0]);
434
435 var failures = [];
436 var completer = new Completer();
437 for (var match in matches) {
438 var future = validate(match);
439
440 future.handleException((e) {
441 failures.add(e);
442 if (failures.length != matches.length) return true;
443
444 var error = new StringBuffer();
445 error.add("No files named $name in $dir were valid:\n");
446 for (var failure in failures) {
447 error.add(" ").add(failure).add("\n");
448 }
449 completer.completeException(new ExpectException(error.toString()));
450 return true;
451 });
452
453 future.then(completer.complete);
454 }
455 return completer.future;
456 });
457 }
367 } 458 }
368 459
369 /** 460 /**
370 * Describes a file. These are used both for setting up an expected directory 461 * Describes a file. These are used both for setting up an expected directory
371 * tree before running a test, and for validating that the file system matches 462 * tree before running a test, and for validating that the file system matches
372 * some expectations after running it. 463 * some expectations after running it.
373 */ 464 */
374 class FileDescriptor extends Descriptor { 465 class FileDescriptor extends Descriptor {
375 /** 466 /**
376 * The text contents of the file. 467 * The text contents of the file.
377 */ 468 */
378 final String contents; 469 final String contents;
379 470
380 FileDescriptor(String name, this.contents) : super(name); 471 FileDescriptor(Pattern name, this.contents) : super(name);
381 472
382 /** 473 /**
383 * Creates the file within [dir]. Returns a [Future] that is completed after 474 * Creates the file within [dir]. Returns a [Future] that is completed after
384 * the creation is done. 475 * the creation is done.
385 */ 476 */
386 Future<File> create(dir) { 477 Future<File> create(dir) {
387 return writeTextFile(join(dir, name), contents); 478 return writeTextFile(join(dir, _stringName), contents);
479 }
480
481 /**
482 * Deletes the file within [dir]. Returns a [Future] that is completed after
483 * the deletion is done.
484 */
485 Future delete(dir) {
486 return deleteFile(join(dir, _stringName));
388 } 487 }
389 488
390 /** 489 /**
391 * Validates that this file correctly matches the actual file at [path]. 490 * Validates that this file correctly matches the actual file at [path].
392 */ 491 */
393 Future validate(String path) { 492 Future validate(String path) {
394 path = join(path, name); 493 return _validateOneMatch(path, (file) {
395 return fileExists(path).chain((exists) { 494 return readTextFile(file).transform((text) {
396 if (!exists) Expect.fail('Expected file $path does not exist.');
397
398 return readTextFile(path).transform((text) {
399 if (text == contents) return null; 495 if (text == contents) return null;
400 496
401 Expect.fail('File $path should contain:\n\n$contents\n\n' 497 Expect.fail('File $file should contain:\n\n$contents\n\n'
402 'but contained:\n\n$text'); 498 'but contained:\n\n$text');
403 }); 499 });
404 }); 500 });
405 } 501 }
406 502
407 /** 503 /**
408 * Loads the contents of the file. 504 * Loads the contents of the file.
409 */ 505 */
410 InputStream load(List<String> path) { 506 InputStream load(List<String> path) {
411 if (!path.isEmpty()) { 507 if (!path.isEmpty()) {
(...skipping 11 matching lines...) Expand all
423 * Describes a directory and its contents. These are used both for setting up 519 * Describes a directory and its contents. These are used both for setting up
424 * an expected directory tree before running a test, and for validating that 520 * an expected directory tree before running a test, and for validating that
425 * the file system matches some expectations after running it. 521 * the file system matches some expectations after running it.
426 */ 522 */
427 class DirectoryDescriptor extends Descriptor { 523 class DirectoryDescriptor extends Descriptor {
428 /** 524 /**
429 * The files and directories contained in this directory. 525 * The files and directories contained in this directory.
430 */ 526 */
431 final List<Descriptor> contents; 527 final List<Descriptor> contents;
432 528
433 DirectoryDescriptor(String name, this.contents) : super(name); 529 DirectoryDescriptor(Pattern name, this.contents) : super(name);
434 530
435 /** 531 /**
436 * Creates the file within [dir]. Returns a [Future] that is completed after 532 * Creates the file within [dir]. Returns a [Future] that is completed after
437 * the creation is done. 533 * the creation is done.
438 */ 534 */
439 Future<Directory> create(parentDir) { 535 Future<Directory> create(parentDir) {
440 final completer = new Completer<Directory>(); 536 final completer = new Completer<Directory>();
441 537
442 // Create the directory. 538 // Create the directory.
443 createDir(join(parentDir, name)).then((dir) { 539 ensureDir(join(parentDir, _stringName)).then((dir) {
444 if (contents == null) { 540 if (contents == null) {
445 completer.complete(dir); 541 completer.complete(dir);
446 } else { 542 } else {
447 // Recursively create all of its children. 543 // Recursively create all of its children.
448 final childFutures = contents.map((child) => child.create(dir)); 544 final childFutures = contents.map((child) => child.create(dir));
449 Futures.wait(childFutures).then((_) { 545 Futures.wait(childFutures).then((_) {
450 // Only complete once all of the children have been created too. 546 // Only complete once all of the children have been created too.
451 completer.complete(dir); 547 completer.complete(dir);
452 }); 548 });
453 } 549 }
454 }); 550 });
455 551
456 return completer.future; 552 return completer.future;
457 } 553 }
458 554
459 /** 555 /**
556 * Deletes the directory within [dir]. Returns a [Future] that is completed
557 * after the deletion is done.
558 */
559 Future delete(dir) {
560 return deleteDir(join(dir, _stringName));
561 }
562
563 /**
460 * Validates that the directory at [path] contains all of the expected 564 * Validates that the directory at [path] contains all of the expected
461 * contents in this descriptor. Note that this does *not* check that the 565 * contents in this descriptor. Note that this does *not* check that the
462 * directory doesn't contain other unexpected stuff, just that it *does* 566 * directory doesn't contain other unexpected stuff, just that it *does*
463 * contain the stuff we do expect. 567 * contain the stuff we do expect.
464 */ 568 */
465 Future validate(String path) { 569 Future validate(String path) {
466 // Validate each of the items in this directory. 570 return _validateOneMatch(path, (dir) {
467 final entryFutures = contents.map( 571 // Validate each of the items in this directory.
468 (entry) => entry.validate(join(path, name))); 572 final entryFutures = contents.map((entry) => entry.validate(dir));
469 573
470 // If they are all valid, the directory is valid. 574 // If they are all valid, the directory is valid.
471 return Futures.wait(entryFutures).transform((entries) => null); 575 return Futures.wait(entryFutures).transform((entries) => null);
576 });
472 } 577 }
473 578
474 /** 579 /**
475 * Loads [path] from within this directory. 580 * Loads [path] from within this directory.
476 */ 581 */
477 InputStream load(List<String> path) { 582 InputStream load(List<String> path) {
478 if (path.isEmpty()) { 583 if (path.isEmpty()) {
479 throw "Can't load the contents of $name: is a directory."; 584 throw "Can't load the contents of $name: is a directory.";
480 } 585 }
481 586
482 for (var descriptor in contents) { 587 for (var descriptor in contents) {
483 if (descriptor.name == path[0]) { 588 if (descriptor.name == path[0]) {
484 return descriptor.load(path.getRange(1, path.length - 1)); 589 return descriptor.load(path.getRange(1, path.length - 1));
485 } 590 }
486 } 591 }
487 592
488 throw "Directory $name doesn't contain ${Strings.join('/', path)}."; 593 throw "Directory $name doesn't contain ${Strings.join('/', path)}.";
489 } 594 }
490 } 595 }
491 596
492 /** 597 /**
493 * Describes a Git repository and its contents. 598 * Describes a Git repository and its contents.
494 */ 599 */
495 class GitRepoDescriptor extends DirectoryDescriptor { 600 class GitRepoDescriptor extends DirectoryDescriptor {
496 GitRepoDescriptor(String name, List<Descriptor> contents) 601 GitRepoDescriptor(Pattern name, List<Descriptor> contents)
497 : super(name, contents); 602 : super(name, contents);
498 603
499 /** 604 /**
500 * Creates the Git repository and commits the contents. 605 * Creates the Git repository and commits the contents.
501 */ 606 */
502 Future<Directory> create(parentDir) { 607 Future<Directory> create(parentDir) {
503 var workingDir; 608 var workingDir;
504 Future runGit(List<String> args) { 609 Future runGit(List<String> args) => _runGit(args, workingDir);
505 return runProcess('git', args, workingDir: workingDir.path).
506 transform((result) {
507 if (!result.success) throw "Error running git: ${result.stderr}";
508 return null;
509 });
510 }
511 610
512 return super.create(parentDir).chain((rootDir) { 611 return super.create(parentDir).chain((rootDir) {
513 workingDir = rootDir; 612 workingDir = rootDir;
514 return runGit(['init']); 613 return runGit(['init']);
515 }).chain((_) => runGit(['add', '.'])) 614 }).chain((_) => runGit(['add', '.']))
516 .chain((_) => runGit(['commit', '-m', 'initial commit'])) 615 .chain((_) => runGit(['commit', '-m', 'initial commit']))
517 .transform((_) => workingDir); 616 .transform((_) => workingDir);
518 } 617 }
618
619 /**
620 * Commits any changes to the Git repository.
621 */
622 Future commit(parentDir) {
Jennifer Messerly 2012/07/03 02:03:10 anyone using this yet?
nweiz 2012/07/03 18:04:49 No, all these changes are going to be used in a fu
623 var workingDir;
624 Future runGit(List<String> args) => _runGit(args, workingDir);
625
626 return super.create(parentDir).chain((rootDir) {
627 workingDir = rootDir;
628 return runGit(['add', '.']);
629 }).chain((_) => runGit(['commit', '-m', 'update']));
630 }
631
632 /**
633 * Schedules changes to be committed to the Git repository.
634 */
635 void scheduleCommit() => _schedule((dir) => this.commit(dir));
636
637 Future _runGit(List<String> args, Directory workingDir) {
638 return runProcess('git', args, workingDir: workingDir.path).
639 transform((result) {
640 if (!result.success) throw "Error running git: ${result.stderr}";
641 return null;
642 });
643 }
519 } 644 }
520 645
521 /** 646 /**
522 * Describes a gzipped tar file and its contents. 647 * Describes a gzipped tar file and its contents.
523 */ 648 */
524 class TarFileDescriptor extends Descriptor { 649 class TarFileDescriptor extends Descriptor {
525 final List<Descriptor> contents; 650 final List<Descriptor> contents;
526 651
527 TarFileDescriptor(String name, this.contents) 652 TarFileDescriptor(Pattern name, this.contents)
528 : super(name); 653 : super(name);
529 654
530 /** 655 /**
531 * Creates the files and directories within this tar file, then archives them, 656 * Creates the files and directories within this tar file, then archives them,
532 * compresses them, and saves the result to [parentDir]. 657 * compresses them, and saves the result to [parentDir].
533 */ 658 */
534 Future<File> create(parentDir) { 659 Future<File> create(parentDir) {
535 var tempDir; 660 var tempDir;
536 return parentDir.createTemp().chain((_tempDir) { 661 return parentDir.createTemp().chain((_tempDir) {
537 tempDir = _tempDir; 662 tempDir = _tempDir;
538 return Futures.wait(contents.map((child) => child.create(tempDir))); 663 return Futures.wait(contents.map((child) => child.create(tempDir)));
539 }).chain((_) { 664 }).chain((_) {
540 var args = ["--directory", tempDir.path, "--create", "--gzip", "--file", 665 var args = ["--directory", tempDir.path, "--create", "--gzip", "--file",
541 join(parentDir, name)]; 666 join(parentDir, _stringName)];
542 args.addAll(contents.map((child) => child.name)); 667 args.addAll(contents.map((child) => child.name));
543 return runProcess("tar", args); 668 return runProcess("tar", args);
544 }).chain((result) { 669 }).chain((result) {
545 if (!result.success) { 670 if (!result.success) {
546 throw "Failed to create tar file $name.\n" 671 throw "Failed to create tar file $name.\n"
547 "STDERR: ${Strings.join(result.stderr, "\n")}"; 672 "STDERR: ${Strings.join(result.stderr, "\n")}";
548 } 673 }
549 return deleteDir(tempDir); 674 return deleteDir(tempDir);
550 }).transform((_) { 675 }).transform((_) {
551 return new File(join(parentDir, name)); 676 return new File(join(parentDir, _stringName));
552 }); 677 });
553 } 678 }
554 679
555 /** 680 /**
556 * Validates that the `.tar.gz` file at [path] contains the expected contents. 681 * Validates that the `.tar.gz` file at [path] contains the expected contents.
557 */ 682 */
558 Future validate(String path) { 683 Future validate(String path) {
559 throw "TODO(nweiz): implement this"; 684 throw "TODO(nweiz): implement this";
560 } 685 }
561 686
(...skipping 15 matching lines...) Expand all
577 }).then((tar) { 702 }).then((tar) {
578 var sourceStream = tar.openInputStream(); 703 var sourceStream = tar.openInputStream();
579 pipeInputToInput( 704 pipeInputToInput(
580 sourceStream, sinkStream, onClosed: tempDir.deleteRecursively); 705 sourceStream, sinkStream, onClosed: tempDir.deleteRecursively);
581 }); 706 });
582 return sinkStream; 707 return sinkStream;
583 } 708 }
584 } 709 }
585 710
586 /** 711 /**
587 * Schedules a callback to be called before Pub is run with [runPub]. 712 * Schedules a callback to be called as part of the test case.
588 */ 713 */
589 void _scheduleBeforePub(_ScheduledEvent event) { 714 void _schedule(_ScheduledEvent event) {
590 if (_scheduledBeforePub == null) _scheduledBeforePub = []; 715 if (_scheduled == null) _scheduled = [];
591 _scheduledBeforePub.add(event); 716 _scheduled.add(event);
592 } 717 }
593 718
594 /** 719 /**
595 * Schedules a callback to be called after Pub is run with [runPub].
596 */
597 void _scheduleAfterPub(_ScheduledEvent event) {
598 if (_scheduledAfterPub == null) _scheduledAfterPub = [];
599 _scheduledAfterPub.add(event);
600 }
601
602 /**
603 * Schedules a callback to be called after Pub is run with [runPub], even if it 720 * Schedules a callback to be called after Pub is run with [runPub], even if it
604 * fails. 721 * fails.
605 */ 722 */
606 void _scheduleCleanup(_ScheduledEvent event) { 723 void _scheduleCleanup(_ScheduledEvent event) {
607 if (_scheduledCleanup == null) _scheduledCleanup = []; 724 if (_scheduledCleanup == null) _scheduledCleanup = [];
608 _scheduledCleanup.add(event); 725 _scheduledCleanup.add(event);
609 } 726 }
OLDNEW
« no previous file with comments | « utils/tests/pub/pub_test.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698