| 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 /// Test infrastructure for testing pub. Unlike typical unit tests, most pub | 5 /// Test infrastructure for testing pub. Unlike typical unit tests, most pub |
| 6 /// tests are integration tests that stage some stuff on the file system, run | 6 /// tests are integration tests that stage some stuff on the file system, run |
| 7 /// pub, and then validate the results. This library provides an API to build | 7 /// pub, and then validate the results. This library provides an API to build |
| 8 /// tests like that. | 8 /// tests like that. |
| 9 library test_pub; | 9 library test_pub; |
| 10 | 10 |
| (...skipping 586 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 597 | 597 |
| 598 pub.writeLine("y"); | 598 pub.writeLine("y"); |
| 599 } | 599 } |
| 600 | 600 |
| 601 | 601 |
| 602 /// Calls [fn] with appropriately modified arguments to run a pub process. [fn] | 602 /// Calls [fn] with appropriately modified arguments to run a pub process. [fn] |
| 603 /// should have the same signature as [startProcess], except that the returned | 603 /// should have the same signature as [startProcess], except that the returned |
| 604 /// [Future] may have a type other than [Process]. | 604 /// [Future] may have a type other than [Process]. |
| 605 Future _doPub(Function fn, sandboxDir, List args, Future<Uri> tokenEndpoint) { | 605 Future _doPub(Function fn, sandboxDir, List args, Future<Uri> tokenEndpoint) { |
| 606 String pathInSandbox(path) => join(getFullPath(sandboxDir), path); | 606 String pathInSandbox(path) => join(getFullPath(sandboxDir), path); |
| 607 | 607 return defer(() { |
| 608 return Future.wait([ | 608 ensureDir(pathInSandbox(appPath)); |
| 609 ensureDir(pathInSandbox(appPath)), | 609 return Future.wait([ |
| 610 _awaitObject(args), | 610 _awaitObject(args), |
| 611 tokenEndpoint == null ? new Future.immediate(null) : tokenEndpoint | 611 tokenEndpoint == null ? new Future.immediate(null) : tokenEndpoint |
| 612 ]).then((results) { | 612 ]); |
| 613 var args = results[1]; | 613 }).then((results) { |
| 614 var tokenEndpoint = results[2]; | 614 var args = results[0]; |
| 615 var tokenEndpoint = results[1]; |
| 615 // Find a Dart executable we can use to spawn. Use the same one that was | 616 // Find a Dart executable we can use to spawn. Use the same one that was |
| 616 // used to run this script itself. | 617 // used to run this script itself. |
| 617 var dartBin = new Options().executable; | 618 var dartBin = new Options().executable; |
| 618 | 619 |
| 619 // If the executable looks like a path, get its full path. That way we | 620 // If the executable looks like a path, get its full path. That way we |
| 620 // can still find it when we spawn it with a different working directory. | 621 // can still find it when we spawn it with a different working directory. |
| 621 if (dartBin.contains(Platform.pathSeparator)) { | 622 if (dartBin.contains(Platform.pathSeparator)) { |
| 622 dartBin = new File(dartBin).fullPathSync(); | 623 dartBin = new File(dartBin).fullPathSync(); |
| 623 } | 624 } |
| 624 | 625 |
| (...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 812 /// directory. | 813 /// directory. |
| 813 void scheduleValidate() => _schedule((parentDir) => validate(parentDir.path)); | 814 void scheduleValidate() => _schedule((parentDir) => validate(parentDir.path)); |
| 814 | 815 |
| 815 /// Asserts that the name of the descriptor is a [String] and returns it. | 816 /// Asserts that the name of the descriptor is a [String] and returns it. |
| 816 String get _stringName { | 817 String get _stringName { |
| 817 if (name is String) return name; | 818 if (name is String) return name; |
| 818 throw 'Pattern $name must be a string.'; | 819 throw 'Pattern $name must be a string.'; |
| 819 } | 820 } |
| 820 | 821 |
| 821 /// Validates that at least one file in [dir] matching [name] is valid | 822 /// Validates that at least one file in [dir] matching [name] is valid |
| 822 /// according to [validate]. [validate] should complete to an exception if | 823 /// according to [validate]. [validate] should throw or complete to an |
| 823 /// the input path is invalid. | 824 /// exception if the input path is invalid. |
| 824 Future _validateOneMatch(String dir, Future validate(String path)) { | 825 Future _validateOneMatch(String dir, Future validate(String path)) { |
| 825 // Special-case strings to support multi-level names like "myapp/packages". | 826 // Special-case strings to support multi-level names like "myapp/packages". |
| 826 if (name is String) { | 827 if (name is String) { |
| 827 var path = join(dir, name); | 828 var path = join(dir, name); |
| 828 return exists(path).then((exists) { | 829 return defer(() { |
| 829 if (!exists) { | 830 if (!entryExists(path)) { |
| 830 throw new ExpectException('File $name in $dir not found.'); | 831 throw new ExpectException('File $name in $dir not found.'); |
| 831 } | 832 } |
| 832 return validate(path); | 833 return validate(path); |
| 833 }); | 834 }); |
| 834 } | 835 } |
| 835 | 836 |
| 836 // TODO(nweiz): remove this when issue 4061 is fixed. | 837 // TODO(nweiz): remove this when issue 4061 is fixed. |
| 837 var stackTrace; | 838 var stackTrace; |
| 838 try { | 839 try { |
| 839 throw ""; | 840 throw ""; |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 890 | 891 |
| 891 String get textContents => new String.fromCharCodes(contents); | 892 String get textContents => new String.fromCharCodes(contents); |
| 892 | 893 |
| 893 FileDescriptor.bytes(Pattern name, this.contents) : super(name); | 894 FileDescriptor.bytes(Pattern name, this.contents) : super(name); |
| 894 | 895 |
| 895 FileDescriptor(Pattern name, String contents) : | 896 FileDescriptor(Pattern name, String contents) : |
| 896 this.bytes(name, encodeUtf8(contents)); | 897 this.bytes(name, encodeUtf8(contents)); |
| 897 | 898 |
| 898 /// Creates the file within [dir]. Returns a [Future] that is completed after | 899 /// Creates the file within [dir]. Returns a [Future] that is completed after |
| 899 /// the creation is done. | 900 /// the creation is done. |
| 900 Future<File> create(dir) => new Future.immediate(null).then((_) => | 901 Future<File> create(dir) => |
| 901 writeBinaryFile(join(dir, _stringName), contents)); | 902 defer(() => writeBinaryFile(join(dir, _stringName), contents)); |
| 902 | 903 |
| 903 /// Deletes the file within [dir]. Returns a [Future] that is completed after | 904 /// Deletes the file within [dir]. Returns a [Future] that is completed after |
| 904 /// the deletion is done. | 905 /// the deletion is done. |
| 905 Future delete(dir) { | 906 Future delete(dir) => |
| 906 return deleteFile(join(dir, _stringName)); | 907 defer(() => deleteFile(join(dir, _stringName))); |
| 907 } | |
| 908 | 908 |
| 909 /// Validates that this file correctly matches the actual file at [path]. | 909 /// Validates that this file correctly matches the actual file at [path]. |
| 910 Future validate(String path) { | 910 Future validate(String path) { |
| 911 return _validateOneMatch(path, (file) { | 911 return _validateOneMatch(path, (file) { |
| 912 return readTextFile(file).then((text) { | 912 var text = readTextFile(file); |
| 913 if (text == textContents) return null; | 913 if (text == textContents) return null; |
| 914 | 914 |
| 915 throw new ExpectException( | 915 throw new ExpectException( |
| 916 'File $file should contain:\n\n$textContents\n\n' | 916 'File $file should contain:\n\n$textContents\n\n' |
| 917 'but contained:\n\n$text'); | 917 'but contained:\n\n$text'); |
| 918 }); | |
| 919 }); | 918 }); |
| 920 } | 919 } |
| 921 | 920 |
| 922 /// Loads the contents of the file. | 921 /// Loads the contents of the file. |
| 923 ByteStream load(List<String> path) { | 922 ByteStream load(List<String> path) { |
| 924 if (!path.isEmpty) { | 923 if (!path.isEmpty) { |
| 925 var joinedPath = Strings.join(path, '/'); | 924 var joinedPath = Strings.join(path, '/'); |
| 926 throw "Can't load $joinedPath from within $name: not a directory."; | 925 throw "Can't load $joinedPath from within $name: not a directory."; |
| 927 } | 926 } |
| 928 | 927 |
| 929 return new ByteStream.fromBytes(contents); | 928 return new ByteStream.fromBytes(contents); |
| 930 } | 929 } |
| 931 } | 930 } |
| 932 | 931 |
| 933 /// Describes a directory and its contents. These are used both for setting up | 932 /// Describes a directory and its contents. These are used both for setting up |
| 934 /// an expected directory tree before running a test, and for validating that | 933 /// an expected directory tree before running a test, and for validating that |
| 935 /// the file system matches some expectations after running it. | 934 /// the file system matches some expectations after running it. |
| 936 class DirectoryDescriptor extends Descriptor { | 935 class DirectoryDescriptor extends Descriptor { |
| 937 /// The files and directories contained in this directory. | 936 /// The files and directories contained in this directory. |
| 938 final List<Descriptor> contents; | 937 final List<Descriptor> contents; |
| 939 | 938 |
| 940 DirectoryDescriptor(Pattern name, List<Descriptor> contents) | 939 DirectoryDescriptor(Pattern name, List<Descriptor> contents) |
| 941 : this.contents = contents == null ? <Descriptor>[] : contents, | 940 : this.contents = contents == null ? <Descriptor>[] : contents, |
| 942 super(name); | 941 super(name); |
| 943 | 942 |
| 944 /// Creates the file within [dir]. Returns a [Future] that is completed after | 943 /// Creates the file within [dir]. Returns a [Future] that is completed after |
| 945 /// the creation is done. | 944 /// the creation is done. |
| 946 Future<Directory> create(parentDir) { | 945 Future<Directory> create(parentDir) { |
| 947 // Create the directory. | 946 return defer(() { |
| 948 return ensureDir(join(parentDir, _stringName)).then((dir) { | 947 // Create the directory. |
| 949 if (contents == null) return new Future<Directory>.immediate(dir); | 948 var dir = ensureDir(join(parentDir, _stringName)); |
| 949 if (contents == null) return dir; |
| 950 | 950 |
| 951 // Recursively create all of its children. | 951 // Recursively create all of its children. |
| 952 final childFutures = | 952 var childFutures = contents.map((child) => child.create(dir)).toList(); |
| 953 contents.map((child) => child.create(dir)).toList(); | |
| 954 // Only complete once all of the children have been created too. | 953 // Only complete once all of the children have been created too. |
| 955 return Future.wait(childFutures).then((_) => dir); | 954 return Future.wait(childFutures).then((_) => dir); |
| 956 }); | 955 }); |
| 957 } | 956 } |
| 958 | 957 |
| 959 /// Deletes the directory within [dir]. Returns a [Future] that is completed | 958 /// Deletes the directory within [dir]. Returns a [Future] that is completed |
| 960 /// after the deletion is done. | 959 /// after the deletion is done. |
| 961 Future delete(dir) { | 960 Future delete(dir) { |
| 962 return deleteDir(join(dir, _stringName)); | 961 return deleteDir(join(dir, _stringName)); |
| 963 } | 962 } |
| (...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1147 } | 1146 } |
| 1148 | 1147 |
| 1149 /// A descriptor that validates that no file exists with the given name. | 1148 /// A descriptor that validates that no file exists with the given name. |
| 1150 class NothingDescriptor extends Descriptor { | 1149 class NothingDescriptor extends Descriptor { |
| 1151 NothingDescriptor(String name) : super(name); | 1150 NothingDescriptor(String name) : super(name); |
| 1152 | 1151 |
| 1153 Future create(dir) => new Future.immediate(null); | 1152 Future create(dir) => new Future.immediate(null); |
| 1154 Future delete(dir) => new Future.immediate(null); | 1153 Future delete(dir) => new Future.immediate(null); |
| 1155 | 1154 |
| 1156 Future validate(String dir) { | 1155 Future validate(String dir) { |
| 1157 return exists(join(dir, name)).then((exists) { | 1156 return defer(() { |
| 1158 if (exists) { | 1157 if (entryExists(join(dir, name))) { |
| 1159 throw new ExpectException('File $name in $dir should not exist.'); | 1158 throw new ExpectException('File $name in $dir should not exist.'); |
| 1160 } | 1159 } |
| 1161 }); | 1160 }); |
| 1162 } | 1161 } |
| 1163 | 1162 |
| 1164 ByteStream load(List<String> path) { | 1163 ByteStream load(List<String> path) { |
| 1165 if (path.isEmpty) { | 1164 if (path.isEmpty) { |
| 1166 throw "Can't load the contents of $name: it doesn't exist."; | 1165 throw "Can't load the contents of $name: it doesn't exist."; |
| 1167 } else { | 1166 } else { |
| 1168 throw "Can't load ${Strings.join(path, '/')} from within $name: $name " | 1167 throw "Can't load ${Strings.join(path, '/')} from within $name: $name " |
| 1169 "doesn't exist."; | 1168 "doesn't exist."; |
| 1170 } | 1169 } |
| 1171 } | 1170 } |
| 1172 } | 1171 } |
| 1173 | 1172 |
| 1174 /// A function that creates a [Validator] subclass. | 1173 /// A function that creates a [Validator] subclass. |
| 1175 typedef Validator ValidatorCreator(Entrypoint entrypoint); | 1174 typedef Validator ValidatorCreator(Entrypoint entrypoint); |
| 1176 | 1175 |
| 1177 /// Schedules a single [Validator] to run on the [appPath]. Returns a scheduled | 1176 /// Schedules a single [Validator] to run on the [appPath]. Returns a scheduled |
| 1178 /// Future that contains the erros and warnings produced by that validator. | 1177 /// Future that contains the erros and warnings produced by that validator. |
| 1179 Future<Pair<List<String>, List<String>>> schedulePackageValidation( | 1178 Future<Pair<List<String>, List<String>>> schedulePackageValidation( |
| 1180 ValidatorCreator fn) { | 1179 ValidatorCreator fn) { |
| 1181 return _scheduleValue((sandboxDir) { | 1180 return _scheduleValue((sandboxDir) { |
| 1182 var cache = new SystemCache.withSources( | 1181 var cache = new SystemCache.withSources(join(sandboxDir, cachePath)); |
| 1183 join(sandboxDir, cachePath)); | |
| 1184 | 1182 |
| 1185 return Entrypoint.load(join(sandboxDir, appPath), cache) | 1183 return defer(() { |
| 1186 .then((entrypoint) { | 1184 var validator = fn(new Entrypoint(join(sandboxDir, appPath), cache)); |
| 1187 var validator = fn(entrypoint); | |
| 1188 return validator.validate().then((_) { | 1185 return validator.validate().then((_) { |
| 1189 return new Pair(validator.errors, validator.warnings); | 1186 return new Pair(validator.errors, validator.warnings); |
| 1190 }); | 1187 }); |
| 1191 }); | 1188 }); |
| 1192 }); | 1189 }); |
| 1193 } | 1190 } |
| 1194 | 1191 |
| 1195 /// A matcher that matches a Pair. | 1192 /// A matcher that matches a Pair. |
| 1196 Matcher pairOf(Matcher firstMatcher, Matcher lastMatcher) => | 1193 Matcher pairOf(Matcher firstMatcher, Matcher lastMatcher) => |
| 1197 new _PairMatcher(firstMatcher, lastMatcher); | 1194 new _PairMatcher(firstMatcher, lastMatcher); |
| (...skipping 324 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1522 } | 1519 } |
| 1523 | 1520 |
| 1524 /// Ignore all requests with the given [method] and [path]. If one is | 1521 /// Ignore all requests with the given [method] and [path]. If one is |
| 1525 /// received, don't respond to it. | 1522 /// received, don't respond to it. |
| 1526 void ignore(String method, String path) => | 1523 void ignore(String method, String path) => |
| 1527 _ignored.add(new Pair(method, path)); | 1524 _ignored.add(new Pair(method, path)); |
| 1528 | 1525 |
| 1529 /// Raises an error complaining of an unexpected request. | 1526 /// Raises an error complaining of an unexpected request. |
| 1530 void _awaitHandle(HttpRequest request, HttpResponse response) { | 1527 void _awaitHandle(HttpRequest request, HttpResponse response) { |
| 1531 if (_ignored.contains(new Pair(request.method, request.path))) return; | 1528 if (_ignored.contains(new Pair(request.method, request.path))) return; |
| 1532 var future = timeout(new Future.immediate(null).then((_) { | 1529 var future = timeout(defer(() { |
| 1533 if (_handlers.isEmpty) { | 1530 if (_handlers.isEmpty) { |
| 1534 fail('Unexpected ${request.method} request to ${request.path}.'); | 1531 fail('Unexpected ${request.method} request to ${request.path}.'); |
| 1535 } | 1532 } |
| 1536 return _handlers.removeFirst(); | 1533 return _handlers.removeFirst(); |
| 1537 }).then((handler) { | 1534 }).then((handler) { |
| 1538 handler(request, response); | 1535 handler(request, response); |
| 1539 }), _SCHEDULE_TIMEOUT, "waiting for a handler for ${request.method} " | 1536 }), _SCHEDULE_TIMEOUT, "waiting for a handler for ${request.method} " |
| 1540 "${request.path}"); | 1537 "${request.path}"); |
| 1541 expect(future, completes); | 1538 expect(future, completes); |
| 1542 } | 1539 } |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1606 /// calling [completion] is unnecessary. | 1603 /// calling [completion] is unnecessary. |
| 1607 void expectLater(Future actual, matcher, {String reason, | 1604 void expectLater(Future actual, matcher, {String reason, |
| 1608 FailureHandler failureHandler, bool verbose: false}) { | 1605 FailureHandler failureHandler, bool verbose: false}) { |
| 1609 _schedule((_) { | 1606 _schedule((_) { |
| 1610 return actual.then((value) { | 1607 return actual.then((value) { |
| 1611 expect(value, matcher, reason: reason, failureHandler: failureHandler, | 1608 expect(value, matcher, reason: reason, failureHandler: failureHandler, |
| 1612 verbose: false); | 1609 verbose: false); |
| 1613 }); | 1610 }); |
| 1614 }); | 1611 }); |
| 1615 } | 1612 } |
| OLD | NEW |