Chromium Code Reviews| 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 |