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 /// Returns the `pub run` process. |
| 398 ScheduledProcess pubRun({bool global: false, Iterable<String> args}) { |
| 399 var pubArgs = global ? ["global", "run"] : ["run"]; |
| 400 pubArgs.addAll(args); |
| 401 var pub = startPub(args: pubArgs); |
| 402 |
| 403 // Loading sources and transformers isn't normally printed, but the pub test |
| 404 // infrastructure runs pub in verbose mode, which enables this. |
| 405 pub.stdout.expect(consumeWhile(startsWith("Loading"))); |
| 406 |
| 407 return pub; |
| 408 } |
| 409 |
380 /// Defines an integration test. | 410 /// Defines an integration test. |
381 /// | 411 /// |
382 /// The [body] should schedule a series of operations which will be run | 412 /// The [body] should schedule a series of operations which will be run |
383 /// asynchronously. | 413 /// asynchronously. |
384 void integration(String description, void body()) => | 414 void integration(String description, void body()) => |
385 _integration(description, body, test); | 415 _integration(description, body, test); |
386 | 416 |
387 /// Like [integration], but causes only this test to run. | 417 /// Like [integration], but causes only this test to run. |
388 void solo_integration(String description, void body()) => | 418 void solo_integration(String description, void body()) => |
389 _integration(description, body, solo_test); | 419 _integration(description, body, solo_test); |
(...skipping 273 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
663 void ensureGit() { | 693 void ensureGit() { |
664 if (Platform.operatingSystem == "windows") { | 694 if (Platform.operatingSystem == "windows") { |
665 currentSchedule.timeout = new Duration(seconds: 30); | 695 currentSchedule.timeout = new Duration(seconds: 30); |
666 } | 696 } |
667 | 697 |
668 if (!gitlib.isInstalled) { | 698 if (!gitlib.isInstalled) { |
669 throw new Exception("Git must be installed to run this test."); | 699 throw new Exception("Git must be installed to run this test."); |
670 } | 700 } |
671 } | 701 } |
672 | 702 |
| 703 /// Schedules activating a global package [package] without running |
| 704 /// "pub global activate". |
| 705 /// |
| 706 /// This is useful because global packages must be hosted, but the test hosted |
| 707 /// server doesn't serve barback. The other parameters here follow |
| 708 /// [createLockFile]. |
| 709 void makeGlobalPackage(String package, String version, |
| 710 Iterable<d.Descriptor> contents, {Iterable<String> pkg, |
| 711 Map<String, String> hosted}) { |
| 712 // Start the server so we know what port to use in the cache directory name. |
| 713 servePackages([]); |
| 714 |
| 715 // Create the package in the hosted cache. |
| 716 d.hostedCache([ |
| 717 d.dir("$package-$version", contents) |
| 718 ]).create(); |
| 719 |
| 720 var lockFile = _createLockFile(pkg: pkg, hosted: hosted); |
| 721 |
| 722 // Add the root package to the lockfile. |
| 723 var id = new PackageId(package, "hosted", new Version.parse(version), |
| 724 package); |
| 725 lockFile.packages[package] = id; |
| 726 |
| 727 // Write the lockfile to the global cache. |
| 728 var sources = new SourceRegistry(); |
| 729 sources.register(new HostedSource()); |
| 730 sources.register(new PathSource()); |
| 731 |
| 732 d.dir(cachePath, [ |
| 733 d.dir("global_packages", [ |
| 734 d.file("$package.lock", lockFile.serialize(null, sources)) |
| 735 ]) |
| 736 ]).create(); |
| 737 } |
| 738 |
673 /// Creates a lock file for [package] without running `pub get`. | 739 /// Creates a lock file for [package] without running `pub get`. |
674 /// | 740 /// |
675 /// [sandbox] is a list of path dependencies to be found in the sandbox | 741 /// [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; | 742 /// 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 | 743 /// each package listed here and all its dependencies will be linked to the |
678 /// version in the Dart repo. | 744 /// version in the Dart repo. |
679 /// | 745 /// |
680 /// [hosted] is a list of package names to version strings for dependencies on | 746 /// [hosted] is a list of package names to version strings for dependencies on |
681 /// hosted packages. | 747 /// hosted packages. |
682 void createLockFile(String package, {Iterable<String> sandbox, | 748 void createLockFile(String package, {Iterable<String> sandbox, |
683 Iterable<String> pkg, Map<String, String> hosted}) { | 749 Iterable<String> pkg, Map<String, String> hosted}) { |
| 750 var lockFile = _createLockFile(sandbox: sandbox, pkg: pkg, hosted: hosted); |
| 751 |
| 752 var sources = new SourceRegistry(); |
| 753 sources.register(new HostedSource()); |
| 754 sources.register(new PathSource()); |
| 755 |
| 756 d.file(path.join(package, 'pubspec.lock'), |
| 757 lockFile.serialize(null, sources)).create(); |
| 758 } |
| 759 |
| 760 /// Creates a lock file for [package] without running `pub get`. |
| 761 /// |
| 762 /// [sandbox] is a list of path dependencies to be found in the sandbox |
| 763 /// directory. [pkg] is a list of packages in the Dart repo's "pkg" directory; |
| 764 /// each package listed here and all its dependencies will be linked to the |
| 765 /// version in the Dart repo. |
| 766 /// |
| 767 /// [hosted] is a list of package names to version strings for dependencies on |
| 768 /// hosted packages. |
| 769 LockFile _createLockFile({Iterable<String> sandbox, |
| 770 Iterable<String> pkg, Map<String, String> hosted}) { |
684 var dependencies = {}; | 771 var dependencies = {}; |
685 | 772 |
686 if (sandbox != null) { | 773 if (sandbox != null) { |
687 for (var package in sandbox) { | 774 for (var package in sandbox) { |
688 dependencies[package] = '../$package'; | 775 dependencies[package] = '../$package'; |
689 } | 776 } |
690 } | 777 } |
691 | 778 |
692 if (pkg != null) { | 779 if (pkg != null) { |
693 _addPackage(String package) { | 780 _addPackage(String package) { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
725 lockFile.packages[name] = id; | 812 lockFile.packages[name] = id; |
726 }); | 813 }); |
727 | 814 |
728 if (hosted != null) { | 815 if (hosted != null) { |
729 hosted.forEach((name, version) { | 816 hosted.forEach((name, version) { |
730 var id = new PackageId(name, 'hosted', new Version.parse(version), name); | 817 var id = new PackageId(name, 'hosted', new Version.parse(version), name); |
731 lockFile.packages[name] = id; | 818 lockFile.packages[name] = id; |
732 }); | 819 }); |
733 } | 820 } |
734 | 821 |
735 var sources = new SourceRegistry(); | 822 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 } | 823 } |
742 | 824 |
743 /// Uses [client] as the mock HTTP client for this test. | 825 /// Uses [client] as the mock HTTP client for this test. |
744 /// | 826 /// |
745 /// Note that this will only affect HTTP requests made via http.dart in the | 827 /// Note that this will only affect HTTP requests made via http.dart in the |
746 /// parent process. | 828 /// parent process. |
747 void useMockClient(MockClient client) { | 829 void useMockClient(MockClient client) { |
748 var oldInnerClient = httpClient.inner; | 830 var oldInnerClient = httpClient.inner; |
749 httpClient.inner = client; | 831 httpClient.inner = client; |
750 currentSchedule.onComplete.schedule(() { | 832 currentSchedule.onComplete.schedule(() { |
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
922 _lastMatcher.matches(item.last, matchState); | 1004 _lastMatcher.matches(item.last, matchState); |
923 } | 1005 } |
924 | 1006 |
925 Description describe(Description description) { | 1007 Description describe(Description description) { |
926 return description.addAll("(", ", ", ")", [_firstMatcher, _lastMatcher]); | 1008 return description.addAll("(", ", ", ")", [_firstMatcher, _lastMatcher]); |
927 } | 1009 } |
928 } | 1010 } |
929 | 1011 |
930 /// A [StreamMatcher] that matches multiple lines of output. | 1012 /// A [StreamMatcher] that matches multiple lines of output. |
931 StreamMatcher emitsLines(String output) => inOrder(output.split("\n")); | 1013 StreamMatcher emitsLines(String output) => inOrder(output.split("\n")); |
OLD | NEW |