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 |