OLD | NEW |
---|---|
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 /// Test infrastructure for testing pub. | 5 /// Test infrastructure for testing pub. |
6 /// | 6 /// |
7 /// Unlike typical unit tests, most pub tests are integration tests that stage | 7 /// Unlike typical unit tests, most pub tests are integration tests that stage |
8 /// some stuff on the file system, run pub, and then validate the results. This | 8 /// some stuff on the file system, run pub, and then validate the results. This |
9 /// library provides an API to build tests like that. | 9 /// library provides an API to build tests like that. |
10 library test_pub; | 10 library test_pub; |
(...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
218 Map<String, List<Map>> _servedPackages; | 218 Map<String, List<Map>> _servedPackages; |
219 | 219 |
220 /// Creates an HTTP server that replicates the structure of pub.dartlang.org. | 220 /// Creates an HTTP server that replicates the structure of pub.dartlang.org. |
221 /// | 221 /// |
222 /// [pubspecs] is a list of unserialized pubspecs representing the packages to | 222 /// [pubspecs] is a list of unserialized pubspecs representing the packages to |
223 /// serve. | 223 /// serve. |
224 /// | 224 /// |
225 /// If [replace] is false, subsequent calls to [servePackages] will add to the | 225 /// If [replace] is false, subsequent calls to [servePackages] will add to the |
226 /// set of packages that are being served. Previous packages will continue to be | 226 /// set of packages that are being served. Previous packages will continue to be |
227 /// served. Otherwise, the previous packages will no longer be served. | 227 /// served. Otherwise, the previous packages will no longer be served. |
228 void servePackages(List<Map> pubspecs, {bool replace: false}) { | 228 /// |
229 /// If [contents] is given, its contents are added to every served | |
230 /// package. | |
231 void servePackages(List<Map> pubspecs, {bool replace: false, | |
232 Iterable<d.Descriptor> contents}) { | |
229 if (_servedPackages == null || _servedPackageDir == null) { | 233 if (_servedPackages == null || _servedPackageDir == null) { |
230 _servedPackages = <String, List<Map>>{}; | 234 _servedPackages = <String, List<Map>>{}; |
231 _servedApiPackageDir = d.dir('packages', []); | 235 _servedApiPackageDir = d.dir('packages', []); |
232 _servedPackageDir = d.dir('packages', []); | 236 _servedPackageDir = d.dir('packages', []); |
233 serve([ | 237 serve([ |
234 d.dir('api', [_servedApiPackageDir]), | 238 d.dir('api', [_servedApiPackageDir]), |
235 _servedPackageDir | 239 _servedPackageDir |
236 ]); | 240 ]); |
237 | 241 |
238 currentSchedule.onComplete.schedule(() { | 242 currentSchedule.onComplete.schedule(() { |
239 _servedPackages = null; | 243 _servedPackages = null; |
240 _servedApiPackageDir = null; | 244 _servedApiPackageDir = null; |
241 _servedPackageDir = null; | 245 _servedPackageDir = null; |
242 }, 'cleaning up served packages'); | 246 }, 'cleaning up served packages'); |
243 } | 247 } |
244 | 248 |
245 schedule(() { | 249 schedule(() { |
246 return awaitObject(pubspecs).then((resolvedPubspecs) { | 250 return awaitObject(pubspecs).then((resolvedPubspecs) { |
247 if (replace) _servedPackages.clear(); | 251 if (replace) _servedPackages.clear(); |
248 | 252 |
249 for (var spec in resolvedPubspecs) { | 253 for (var pubspec in resolvedPubspecs) { |
250 var name = spec['name']; | 254 var name = pubspec['name']; |
251 var version = spec['version']; | 255 var version = pubspec['version']; |
252 var versions = _servedPackages.putIfAbsent(name, () => []); | 256 var versions = _servedPackages.putIfAbsent(name, () => []); |
253 versions.add(spec); | 257 versions.add(pubspec); |
254 } | 258 } |
255 | 259 |
256 _servedApiPackageDir.contents.clear(); | 260 _servedApiPackageDir.contents.clear(); |
257 _servedPackageDir.contents.clear(); | 261 _servedPackageDir.contents.clear(); |
258 for (var name in _servedPackages.keys) { | 262 for (var name in _servedPackages.keys) { |
259 _servedApiPackageDir.contents.addAll([ | 263 _servedApiPackageDir.contents.addAll([ |
260 d.file('$name', JSON.encode({ | 264 d.file('$name', JSON.encode({ |
261 'name': name, | 265 'name': name, |
262 'uploaders': ['nweiz@google.com'], | 266 'uploaders': ['nweiz@google.com'], |
263 'versions': _servedPackages[name].map(packageVersionApiMap).toList() | 267 'versions': _servedPackages[name].map(packageVersionApiMap).toList() |
264 })), | 268 })), |
265 d.dir(name, [ | 269 d.dir(name, [ |
266 d.dir('versions', _servedPackages[name].map((pubspec) { | 270 d.dir('versions', _servedPackages[name].map((pubspec) { |
267 return d.file(pubspec['version'], JSON.encode( | 271 return d.file(pubspec['version'], JSON.encode( |
268 packageVersionApiMap(pubspec, full: true))); | 272 packageVersionApiMap(pubspec, full: true))); |
269 })) | 273 })) |
270 ]) | 274 ]) |
271 ]); | 275 ]); |
272 | 276 |
273 _servedPackageDir.contents.add(d.dir(name, [ | 277 _servedPackageDir.contents.add(d.dir(name, [ |
274 d.dir('versions', _servedPackages[name].map((pubspec) { | 278 d.dir('versions', _servedPackages[name].map((pubspec) { |
275 var version = pubspec['version']; | 279 var version = pubspec['version']; |
276 return d.tar('$version.tar.gz', [ | 280 |
277 d.file('pubspec.yaml', JSON.encode(pubspec)), | 281 var archiveContents = [ |
278 d.libDir(name, '$name $version') | 282 d.file('pubspec.yaml', JSON.encode(pubspec)), |
279 ]); | 283 d.libDir(name, '$name $version') |
284 ]; | |
285 | |
286 if (contents != null) { | |
287 archiveContents.addAll(contents); | |
288 } | |
289 | |
290 return d.tar('$version.tar.gz', archiveContents); | |
280 })) | 291 })) |
281 ])); | 292 ])); |
282 } | 293 } |
283 }); | 294 }); |
284 }, 'initializing the package server'); | 295 }, 'initializing the package server'); |
285 } | 296 } |
286 | 297 |
287 /// Converts [value] into a YAML string. | 298 /// Converts [value] into a YAML string. |
288 String yaml(value) => JSON.encode(value); | 299 String yaml(value) => JSON.encode(value); |
289 | 300 |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
370 void pubGet({Iterable<String> args, output, error, warning, int exitCode}) { | 381 void pubGet({Iterable<String> args, output, error, warning, int exitCode}) { |
371 pubCommand(RunCommand.get, args: args, output: output, error: error, | 382 pubCommand(RunCommand.get, args: args, output: output, error: error, |
372 warning: warning, exitCode: exitCode); | 383 warning: warning, exitCode: exitCode); |
373 } | 384 } |
374 | 385 |
375 void pubUpgrade({Iterable<String> args, output, error, warning, int exitCode}) { | 386 void pubUpgrade({Iterable<String> args, output, error, warning, int exitCode}) { |
376 pubCommand(RunCommand.upgrade, args: args, output: output, error: error, | 387 pubCommand(RunCommand.upgrade, args: args, output: output, error: error, |
377 warning: warning, exitCode: exitCode); | 388 warning: warning, exitCode: exitCode); |
378 } | 389 } |
379 | 390 |
391 /// Schedules starting the "pub [global] run" process and validates the | |
392 /// expected startup output. | |
393 /// | |
394 /// If [global] is `true`, this invokes "pub global run", otherwise it does | |
395 /// "pub run". | |
396 /// | |
397 /// if [transformers] is given, it should contain a list of transformer IDs | |
398 /// (like "myapp/src/transformer") and this will validate that the output for | |
399 /// loading those is shown. | |
400 /// | |
401 /// Returns the `pub run` process. | |
402 ScheduledProcess pubRun({bool global: false, Iterable<String> args, | |
403 Iterable<String> transformers}) { | |
404 var pubArgs = global ? ["global", "run"] : ["run"]; | |
405 pubArgs.addAll(args); | |
406 var pub = startPub(args: pubArgs); | |
407 | |
408 // This isn't normally printed, but the pub test infrastructure runs pub in | |
409 // verbose mode, which enables this. | |
410 pub.stdout.expect(startsWith("Loading source assets")); | |
411 | |
412 if (transformers != null) { | |
413 for (var transformer in transformers) { | |
414 pub.stdout.expect(startsWith("Loading $transformer transformers")); | |
nweiz
2014/07/02 00:12:03
Rather than requiring tests to pass in a list of t
Bob Nystrom
2014/07/02 18:00:53
Done.
| |
415 } | |
416 } | |
417 return pub; | |
418 } | |
419 | |
380 /// Defines an integration test. | 420 /// Defines an integration test. |
381 /// | 421 /// |
382 /// The [body] should schedule a series of operations which will be run | 422 /// The [body] should schedule a series of operations which will be run |
383 /// asynchronously. | 423 /// asynchronously. |
384 void integration(String description, void body()) => | 424 void integration(String description, void body()) => |
385 _integration(description, body, test); | 425 _integration(description, body, test); |
386 | 426 |
387 /// Like [integration], but causes only this test to run. | 427 /// Like [integration], but causes only this test to run. |
388 void solo_integration(String description, void body()) => | 428 void solo_integration(String description, void body()) => |
389 _integration(description, body, solo_test); | 429 _integration(description, body, solo_test); |
(...skipping 273 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
663 void ensureGit() { | 703 void ensureGit() { |
664 if (Platform.operatingSystem == "windows") { | 704 if (Platform.operatingSystem == "windows") { |
665 currentSchedule.timeout = new Duration(seconds: 30); | 705 currentSchedule.timeout = new Duration(seconds: 30); |
666 } | 706 } |
667 | 707 |
668 if (!gitlib.isInstalled) { | 708 if (!gitlib.isInstalled) { |
669 throw new Exception("Git must be installed to run this test."); | 709 throw new Exception("Git must be installed to run this test."); |
670 } | 710 } |
671 } | 711 } |
672 | 712 |
713 /// Schedules activating a global package [package] without running | |
714 /// "pub global activate". | |
715 /// | |
716 /// This is useful because global packages must be hosted, but the test hosted | |
717 /// server doesn't serve barback. The other parameters here follow | |
718 /// [createLockFile]. | |
719 void makeGlobalPackage(String package, String version, | |
720 Iterable<d.Descriptor> contents, {Iterable<String> pkg, | |
721 Map<String, String> hosted}) { | |
722 // Start the server so we know what port to use in the cache directory name. | |
723 servePackages([]); | |
724 | |
725 // Create the package in the hosted cache. | |
726 d.hostedCache([ | |
727 d.dir("$package-$version", contents) | |
728 ]).create(); | |
729 | |
730 var lockFile = _createLockFile(pkg: pkg, hosted: hosted); | |
731 | |
732 // Add the root package to the lockfile. | |
733 var id = new PackageId(package, "hosted", new Version.parse(version), | |
734 package); | |
735 lockFile.packages[package] = id; | |
736 | |
737 // Write the lockfile to the global cache. | |
738 var sources = new SourceRegistry(); | |
739 sources.register(new HostedSource()); | |
740 sources.register(new PathSource()); | |
741 | |
742 d.dir(cachePath, [ | |
743 d.dir("global_packages", [ | |
744 d.file("$package.lock", lockFile.serialize(null, sources)) | |
745 ]) | |
746 ]).create(); | |
747 } | |
748 | |
673 /// Creates a lock file for [package] without running `pub get`. | 749 /// Creates a lock file for [package] without running `pub get`. |
674 /// | 750 /// |
675 /// [sandbox] is a list of path dependencies to be found in the sandbox | 751 /// [sandbox] is a list of path dependencies to be found in the sandbox |
676 /// directory. [pkg] is a list of packages in the Dart repo's "pkg" directory; | 752 /// directory. [pkg] is a list of packages in the Dart repo's "pkg" directory; |
677 /// each package listed here and all its dependencies will be linked to the | 753 /// each package listed here and all its dependencies will be linked to the |
678 /// version in the Dart repo. | 754 /// version in the Dart repo. |
679 /// | 755 /// |
680 /// [hosted] is a list of package names to version strings for dependencies on | 756 /// [hosted] is a list of package names to version strings for dependencies on |
681 /// hosted packages. | 757 /// hosted packages. |
682 void createLockFile(String package, {Iterable<String> sandbox, | 758 void createLockFile(String package, {Iterable<String> sandbox, |
683 Iterable<String> pkg, Map<String, String> hosted}) { | 759 Iterable<String> pkg, Map<String, String> hosted}) { |
760 var lockFile = _createLockFile(sandbox: sandbox, pkg: pkg, hosted: hosted); | |
761 | |
762 var sources = new SourceRegistry(); | |
763 sources.register(new HostedSource()); | |
764 sources.register(new PathSource()); | |
765 | |
766 d.file(path.join(package, 'pubspec.lock'), | |
767 lockFile.serialize(null, sources)).create(); | |
768 } | |
769 | |
770 /// Creates a lock file for [package] without running `pub get`. | |
771 /// | |
772 /// [sandbox] is a list of path dependencies to be found in the sandbox | |
773 /// directory. [pkg] is a list of packages in the Dart repo's "pkg" directory; | |
774 /// each package listed here and all its dependencies will be linked to the | |
775 /// version in the Dart repo. | |
776 /// | |
777 /// [hosted] is a list of package names to version strings for dependencies on | |
778 /// hosted packages. | |
779 LockFile _createLockFile({Iterable<String> sandbox, | |
780 Iterable<String> pkg, Map<String, String> hosted}) { | |
684 var dependencies = {}; | 781 var dependencies = {}; |
685 | 782 |
686 if (sandbox != null) { | 783 if (sandbox != null) { |
687 for (var package in sandbox) { | 784 for (var package in sandbox) { |
688 dependencies[package] = '../$package'; | 785 dependencies[package] = '../$package'; |
689 } | 786 } |
690 } | 787 } |
691 | 788 |
692 if (pkg != null) { | 789 if (pkg != null) { |
693 _addPackage(String package) { | 790 _addPackage(String package) { |
694 if (dependencies.containsKey(package)) return; | 791 if (dependencies.containsKey(package)) return; |
695 | 792 |
696 var packagePath; | 793 var packagePath; |
697 if (package == 'barback') { | 794 if (package == 'barback') { |
698 if (_barbackDir == null) { | 795 if (_barbackDir == null) { |
699 throw new StateError("createLockFile() can only create a lock file " | 796 throw new StateError("createLockFile() can only create a lock file " |
700 "with a barback dependency within a withBarbackVersions() " | 797 "with a barback dependency within a withBarbackVersions() " |
701 "block."); | 798 "block."); |
nweiz
2014/07/02 00:12:03
Fix this indentation.
Bob Nystrom
2014/07/02 18:00:53
Done.
| |
702 } | 799 } |
703 packagePath = _barbackDir; | 800 packagePath = _barbackDir; |
704 } else { | 801 } else { |
705 packagePath = path.join(pkgPath, package); | 802 packagePath = path.join(pkgPath, package); |
706 } | 803 } |
707 | 804 |
708 dependencies[package] = packagePath; | 805 dependencies[package] = packagePath; |
709 var pubspec = loadYaml( | 806 var pubspec = loadYaml( |
710 readTextFile(path.join(packagePath, 'pubspec.yaml'))); | 807 readTextFile(path.join(packagePath, 'pubspec.yaml'))); |
nweiz
2014/07/02 00:12:02
Also this indentation.
Bob Nystrom
2014/07/02 18:00:53
Done.
| |
711 var packageDeps = pubspec['dependencies']; | 808 var packageDeps = pubspec['dependencies']; |
712 if (packageDeps == null) return; | 809 if (packageDeps == null) return; |
713 packageDeps.keys.forEach(_addPackage); | 810 packageDeps.keys.forEach(_addPackage); |
714 } | 811 } |
715 | 812 |
716 pkg.forEach(_addPackage); | 813 pkg.forEach(_addPackage); |
717 } | 814 } |
718 | 815 |
719 var lockFile = new LockFile.empty(); | 816 var lockFile = new LockFile.empty(); |
720 dependencies.forEach((name, dependencyPath) { | 817 dependencies.forEach((name, dependencyPath) { |
721 var id = new PackageId(name, 'path', new Version(0, 0, 0), { | 818 var id = new PackageId(name, 'path', new Version(0, 0, 0), { |
722 'path': dependencyPath, | 819 'path': dependencyPath, |
723 'relative': path.isRelative(dependencyPath) | 820 'relative': path.isRelative(dependencyPath) |
724 }); | 821 }); |
725 lockFile.packages[name] = id; | 822 lockFile.packages[name] = id; |
726 }); | 823 }); |
727 | 824 |
728 if (hosted != null) { | 825 if (hosted != null) { |
729 hosted.forEach((name, version) { | 826 hosted.forEach((name, version) { |
730 var id = new PackageId(name, 'hosted', new Version.parse(version), name); | 827 var id = new PackageId(name, 'hosted', new Version.parse(version), name); |
731 lockFile.packages[name] = id; | 828 lockFile.packages[name] = id; |
732 }); | 829 }); |
733 } | 830 } |
734 | 831 |
735 var sources = new SourceRegistry(); | 832 return lockFile; |
736 sources.register(new HostedSource()); | |
737 sources.register(new PathSource()); | |
738 | |
739 d.file(path.join(package, 'pubspec.lock'), | |
740 lockFile.serialize(null, sources)).create(); | |
741 } | 833 } |
742 | 834 |
743 /// Uses [client] as the mock HTTP client for this test. | 835 /// Uses [client] as the mock HTTP client for this test. |
744 /// | 836 /// |
745 /// Note that this will only affect HTTP requests made via http.dart in the | 837 /// Note that this will only affect HTTP requests made via http.dart in the |
746 /// parent process. | 838 /// parent process. |
747 void useMockClient(MockClient client) { | 839 void useMockClient(MockClient client) { |
748 var oldInnerClient = httpClient.inner; | 840 var oldInnerClient = httpClient.inner; |
749 httpClient.inner = client; | 841 httpClient.inner = client; |
750 currentSchedule.onComplete.schedule(() { | 842 currentSchedule.onComplete.schedule(() { |
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
922 _lastMatcher.matches(item.last, matchState); | 1014 _lastMatcher.matches(item.last, matchState); |
923 } | 1015 } |
924 | 1016 |
925 Description describe(Description description) { | 1017 Description describe(Description description) { |
926 return description.addAll("(", ", ", ")", [_firstMatcher, _lastMatcher]); | 1018 return description.addAll("(", ", ", ")", [_firstMatcher, _lastMatcher]); |
927 } | 1019 } |
928 } | 1020 } |
929 | 1021 |
930 /// A [StreamMatcher] that matches multiple lines of output. | 1022 /// A [StreamMatcher] that matches multiple lines of output. |
931 StreamMatcher emitsLines(String output) => inOrder(output.split("\n")); | 1023 StreamMatcher emitsLines(String output) => inOrder(output.split("\n")); |
OLD | NEW |