OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * Test infrastructure for testing pub. Unlike typical unit tests, most pub | 6 * Test infrastructure for testing pub. Unlike typical unit tests, most pub |
7 * tests are integration tests that stage some stuff on the file system, run | 7 * tests are integration tests that stage some stuff on the file system, run |
8 * pub, and then validate the results. This library provides an API to build | 8 * pub, and then validate the results. This library provides an API to build |
9 * tests like that. | 9 * tests like that. |
10 */ | 10 */ |
(...skipping 16 matching lines...) Expand all Loading... |
27 FileDescriptor file(Pattern name, String contents) => | 27 FileDescriptor file(Pattern name, String contents) => |
28 new FileDescriptor(name, contents); | 28 new FileDescriptor(name, contents); |
29 | 29 |
30 /** | 30 /** |
31 * Creates a new [DirectoryDescriptor] with [name] and [contents]. | 31 * Creates a new [DirectoryDescriptor] with [name] and [contents]. |
32 */ | 32 */ |
33 DirectoryDescriptor dir(Pattern name, [List<Descriptor> contents]) => | 33 DirectoryDescriptor dir(Pattern name, [List<Descriptor> contents]) => |
34 new DirectoryDescriptor(name, contents); | 34 new DirectoryDescriptor(name, contents); |
35 | 35 |
36 /** | 36 /** |
| 37 * Creates a new [FutureDescriptor] wrapping [future]. |
| 38 */ |
| 39 FutureDescriptor async(Future<Descriptor> future) => |
| 40 new FutureDescriptor(future); |
| 41 |
| 42 /** |
37 * Creates a new [GitRepoDescriptor] with [name] and [contents]. | 43 * Creates a new [GitRepoDescriptor] with [name] and [contents]. |
38 */ | 44 */ |
39 DirectoryDescriptor git(Pattern name, [List<Descriptor> contents]) => | 45 GitRepoDescriptor git(Pattern name, [List<Descriptor> contents]) => |
40 new GitRepoDescriptor(name, contents); | 46 new GitRepoDescriptor(name, contents); |
41 | 47 |
42 /** | 48 /** |
43 * Creates a new [TarFileDescriptor] with [name] and [contents]. | 49 * Creates a new [TarFileDescriptor] with [name] and [contents]. |
44 */ | 50 */ |
45 TarFileDescriptor tar(Pattern name, [List<Descriptor> contents]) => | 51 TarFileDescriptor tar(Pattern name, [List<Descriptor> contents]) => |
46 new TarFileDescriptor(name, contents); | 52 new TarFileDescriptor(name, contents); |
47 | 53 |
48 /** | 54 /** |
49 * Creates an HTTP server to serve [contents] as static files. This server will | 55 * Creates an HTTP server to serve [contents] as static files. This server will |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
162 */ | 168 */ |
163 List<_ScheduledEvent> _scheduled; | 169 List<_ScheduledEvent> _scheduled; |
164 | 170 |
165 /** | 171 /** |
166 * The list of events that are scheduled to run after the test case, even if it | 172 * The list of events that are scheduled to run after the test case, even if it |
167 * failed. | 173 * failed. |
168 */ | 174 */ |
169 List<_ScheduledEvent> _scheduledCleanup; | 175 List<_ScheduledEvent> _scheduledCleanup; |
170 | 176 |
171 /** | 177 /** |
| 178 * Set to true when the current batch of scheduled events should be aborted. |
| 179 */ |
| 180 bool _abortScheduled = false; |
| 181 |
| 182 /** |
172 * Runs all the scheduled events for a test case. This should only be called | 183 * Runs all the scheduled events for a test case. This should only be called |
173 * once per test case. | 184 * once per test case. |
174 */ | 185 */ |
175 void run() { | 186 void run() { |
176 var createdSandboxDir; | 187 var createdSandboxDir; |
177 | 188 |
178 var asyncDone = expectAsync0(() {}); | 189 var asyncDone = expectAsync0(() {}); |
179 | 190 |
180 Future cleanup() { | 191 Future cleanup() { |
181 return _runScheduled(createdSandboxDir, _scheduledCleanup).chain((_) { | 192 return _runScheduled(createdSandboxDir, _scheduledCleanup).chain((_) { |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
251 * A shorthand for [schedulePub] and [run] when no validation needs to be done | 262 * A shorthand for [schedulePub] and [run] when no validation needs to be done |
252 * after Pub has been run. | 263 * after Pub has been run. |
253 */ | 264 */ |
254 void runPub([List<String> args, Pattern output, Pattern error, | 265 void runPub([List<String> args, Pattern output, Pattern error, |
255 int exitCode = 0]) { | 266 int exitCode = 0]) { |
256 schedulePub(args, output, error, exitCode); | 267 schedulePub(args, output, error, exitCode); |
257 run(); | 268 run(); |
258 } | 269 } |
259 | 270 |
260 /** | 271 /** |
261 * Wraps a test that needs git in order to run. This validates that the test is | 272 * Skips the current test if Git is not installed. This validates that the |
262 * running on a builbot in which case we expect git to be installed. If we are | 273 * current test is running on a buildbot in which case we expect git to be |
263 * not running on the buildbot, we will instead see if git is installed and | 274 * installed. If we are not running on the buildbot, we will instead see if git |
264 * skip the test if not. This way, users don't need to have git installed to | 275 * is installed and skip the test if not. This way, users don't need to have git |
265 * run the tests locally (unless they actually care about the pub git tests). | 276 * installed to run the tests locally (unless they actually care about the pub |
| 277 * git tests). |
266 */ | 278 */ |
267 void withGit(void callback()) { | 279 void ensureGit() { |
268 isGitInstalled.then(expectAsync1((installed) { | 280 _schedule((_) { |
269 if (installed || Platform.environment.containsKey('BUILDBOT_BUILDERNAME')) { | 281 return isGitInstalled.transform((installed) { |
270 callback(); | 282 if (!installed && |
271 } | 283 !Platform.environment.containsKey('BUILDBOT_BUILDERNAME')) { |
272 })); | 284 _abortScheduled = true; |
| 285 } |
| 286 return null; |
| 287 }); |
| 288 }); |
273 } | 289 } |
274 | 290 |
275 Future<Directory> _setUpSandbox() { | 291 Future<Directory> _setUpSandbox() { |
276 return createTempDir('pub-test-sandbox-'); | 292 return createTempDir('pub-test-sandbox-'); |
277 } | 293 } |
278 | 294 |
279 Future _runScheduled(Directory parentDir, List<_ScheduledEvent> scheduled) { | 295 Future _runScheduled(Directory parentDir, List<_ScheduledEvent> scheduled) { |
280 if (scheduled == null) return new Future.immediate(null); | 296 if (scheduled == null) return new Future.immediate(null); |
281 var iterator = scheduled.iterator(); | 297 var iterator = scheduled.iterator(); |
282 | 298 |
283 Future runNextEvent([_]) { | 299 Future runNextEvent([_]) { |
284 if (!iterator.hasNext()) { | 300 if (_abortScheduled || !iterator.hasNext()) { |
| 301 _abortScheduled = false; |
285 scheduled.clear(); | 302 scheduled.clear(); |
286 return new Future.immediate(null); | 303 return new Future.immediate(null); |
287 } | 304 } |
288 | 305 |
289 var future = iterator.next()(parentDir); | 306 var future = iterator.next()(parentDir); |
290 if (future != null) { | 307 if (future != null) { |
291 return future.chain(runNextEvent); | 308 return future.chain(runNextEvent); |
292 } else { | 309 } else { |
293 return runNextEvent(); | 310 return runNextEvent(); |
294 } | 311 } |
(...skipping 300 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
595 if (descriptor.name == path[0]) { | 612 if (descriptor.name == path[0]) { |
596 return descriptor.load(path.getRange(1, path.length - 1)); | 613 return descriptor.load(path.getRange(1, path.length - 1)); |
597 } | 614 } |
598 } | 615 } |
599 | 616 |
600 throw "Directory $name doesn't contain ${Strings.join(path, '/')}."; | 617 throw "Directory $name doesn't contain ${Strings.join(path, '/')}."; |
601 } | 618 } |
602 } | 619 } |
603 | 620 |
604 /** | 621 /** |
| 622 * Wraps a [Future] that will complete to a [Descriptor] and makes it behave |
| 623 * like a concrete [Descriptor]. This is necessary when the contents of the |
| 624 * descriptor depends on information that's not available until part of the test |
| 625 * run is completed. |
| 626 */ |
| 627 class FutureDescriptor extends Descriptor { |
| 628 Future<Descriptor> _future; |
| 629 |
| 630 FutureDescriptor(this._future) : super('<unknown>'); |
| 631 |
| 632 Future create(dir) => _future.chain((desc) => desc.create(dir)); |
| 633 |
| 634 Future validate(dir) => _future.chain((desc) => desc.validate(dir)); |
| 635 |
| 636 Future delete(dir) => _future.chain((desc) => desc.delete(dir)); |
| 637 |
| 638 InputStream load(List<String> path) { |
| 639 var resultStream = new ListInputStream(); |
| 640 _future.then((desc) => pipeInputToInput(desc.load(path), resultStream)); |
| 641 return resultStream; |
| 642 } |
| 643 } |
| 644 |
| 645 /** |
605 * Describes a Git repository and its contents. | 646 * Describes a Git repository and its contents. |
606 */ | 647 */ |
607 class GitRepoDescriptor extends DirectoryDescriptor { | 648 class GitRepoDescriptor extends DirectoryDescriptor { |
608 GitRepoDescriptor(Pattern name, List<Descriptor> contents) | 649 GitRepoDescriptor(Pattern name, List<Descriptor> contents) |
609 : super(name, contents); | 650 : super(name, contents); |
610 | 651 |
611 /** | 652 /** |
612 * Creates the Git repository and commits the contents. | 653 * Creates the Git repository and commits the contents. |
613 */ | 654 */ |
614 Future<Directory> create(parentDir) { | 655 Future<Directory> create(parentDir) { |
(...skipping 19 matching lines...) Expand all Loading... |
634 workingDir = rootDir; | 675 workingDir = rootDir; |
635 return runGit(['add', '.']); | 676 return runGit(['add', '.']); |
636 }).chain((_) => runGit(['commit', '-m', 'update'])); | 677 }).chain((_) => runGit(['commit', '-m', 'update'])); |
637 } | 678 } |
638 | 679 |
639 /** | 680 /** |
640 * Schedules changes to be committed to the Git repository. | 681 * Schedules changes to be committed to the Git repository. |
641 */ | 682 */ |
642 void scheduleCommit() => _schedule((dir) => this.commit(dir)); | 683 void scheduleCommit() => _schedule((dir) => this.commit(dir)); |
643 | 684 |
644 Future _runGit(List<String> args, Directory workingDir) { | 685 /** |
| 686 * Return a Future that completes to the commit in the git repository referred |
| 687 * to by [ref] at the current point in the scheduled test run. |
| 688 */ |
| 689 Future<String> revParse(String ref) { |
| 690 var completer = new Completer<String>(); |
| 691 // TODO(nweiz): inline this once issue 3197 is fixed |
| 692 var superCreate = super.create; |
| 693 _schedule((parentDir) { |
| 694 return superCreate(parentDir).chain((rootDir) { |
| 695 return _runGit(['rev-parse', ref], rootDir); |
| 696 }).transform((output) { |
| 697 completer.complete(output[0]); |
| 698 return null; |
| 699 }); |
| 700 }); |
| 701 return completer.future; |
| 702 } |
| 703 |
| 704 Future<String> _runGit(List<String> args, Directory workingDir) { |
645 return runProcess('git', args, workingDir: workingDir.path). | 705 return runProcess('git', args, workingDir: workingDir.path). |
646 transform((result) { | 706 transform((result) { |
647 if (!result.success) throw "Error running git: ${result.stderr}"; | 707 if (!result.success) throw "Error running git: ${result.stderr}"; |
648 return null; | 708 return result.stdout; |
649 }); | 709 }); |
650 } | 710 } |
651 } | 711 } |
652 | 712 |
653 /** | 713 /** |
654 * Describes a gzipped tar file and its contents. | 714 * Describes a gzipped tar file and its contents. |
655 */ | 715 */ |
656 class TarFileDescriptor extends Descriptor { | 716 class TarFileDescriptor extends Descriptor { |
657 final List<Descriptor> contents; | 717 final List<Descriptor> contents; |
658 | 718 |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
724 } | 784 } |
725 | 785 |
726 /** | 786 /** |
727 * Schedules a callback to be called after Pub is run with [runPub], even if it | 787 * Schedules a callback to be called after Pub is run with [runPub], even if it |
728 * fails. | 788 * fails. |
729 */ | 789 */ |
730 void _scheduleCleanup(_ScheduledEvent event) { | 790 void _scheduleCleanup(_ScheduledEvent event) { |
731 if (_scheduledCleanup == null) _scheduledCleanup = []; | 791 if (_scheduledCleanup == null) _scheduledCleanup = []; |
732 _scheduledCleanup.add(event); | 792 _scheduledCleanup.add(event); |
733 } | 793 } |
OLD | NEW |