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 567 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
578 | 578 |
579 pub.writeLine("y"); | 579 pub.writeLine("y"); |
580 } | 580 } |
581 | 581 |
582 | 582 |
583 /// Calls [fn] with appropriately modified arguments to run a pub process. [fn] | 583 /// Calls [fn] with appropriately modified arguments to run a pub process. [fn] |
584 /// should have the same signature as [startProcess], except that the returned | 584 /// should have the same signature as [startProcess], except that the returned |
585 /// [Future] may have a type other than [Process]. | 585 /// [Future] may have a type other than [Process]. |
586 Future _doPub(Function fn, sandboxDir, List args, Future<Uri> tokenEndpoint) { | 586 Future _doPub(Function fn, sandboxDir, List args, Future<Uri> tokenEndpoint) { |
587 String pathInSandbox(path) => join(getFullPath(sandboxDir), path); | 587 String pathInSandbox(path) => join(getFullPath(sandboxDir), path); |
588 | 588 // Make sure all errors propagate through future. |
589 return Future.wait([ | 589 return defer(() { |
590 ensureDir(pathInSandbox(appPath)), | 590 ensureDir(pathInSandbox(appPath)); |
591 _awaitObject(args), | 591 return Future.wait([ |
592 tokenEndpoint == null ? new Future.immediate(null) : tokenEndpoint | 592 _awaitObject(args), |
593 ]).then((results) { | 593 tokenEndpoint == null ? new Future.immediate(null) : tokenEndpoint |
594 var args = results[1]; | 594 ]); |
595 var tokenEndpoint = results[2]; | 595 }).then((results) { |
| 596 var args = results[0]; |
| 597 var tokenEndpoint = results[1]; |
596 // Find a Dart executable we can use to spawn. Use the same one that was | 598 // Find a Dart executable we can use to spawn. Use the same one that was |
597 // used to run this script itself. | 599 // used to run this script itself. |
598 var dartBin = new Options().executable; | 600 var dartBin = new Options().executable; |
599 | 601 |
600 // If the executable looks like a path, get its full path. That way we | 602 // If the executable looks like a path, get its full path. That way we |
601 // can still find it when we spawn it with a different working directory. | 603 // can still find it when we spawn it with a different working directory. |
602 if (dartBin.contains(Platform.pathSeparator)) { | 604 if (dartBin.contains(Platform.pathSeparator)) { |
603 dartBin = new File(dartBin).fullPathSync(); | 605 dartBin = new File(dartBin).fullPathSync(); |
604 } | 606 } |
605 | 607 |
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
793 /// directory. | 795 /// directory. |
794 void scheduleValidate() => _schedule((parentDir) => validate(parentDir.path)); | 796 void scheduleValidate() => _schedule((parentDir) => validate(parentDir.path)); |
795 | 797 |
796 /// Asserts that the name of the descriptor is a [String] and returns it. | 798 /// Asserts that the name of the descriptor is a [String] and returns it. |
797 String get _stringName { | 799 String get _stringName { |
798 if (name is String) return name; | 800 if (name is String) return name; |
799 throw 'Pattern $name must be a string.'; | 801 throw 'Pattern $name must be a string.'; |
800 } | 802 } |
801 | 803 |
802 /// Validates that at least one file in [dir] matching [name] is valid | 804 /// Validates that at least one file in [dir] matching [name] is valid |
803 /// according to [validate]. [validate] should complete to an exception if | 805 /// according to [validate]. [validate] should throw or complete to an |
804 /// the input path is invalid. | 806 /// exception if the input path is invalid. |
805 Future _validateOneMatch(String dir, Future validate(String path)) { | 807 Future _validateOneMatch(String dir, Future validate(String path)) { |
806 // Special-case strings to support multi-level names like "myapp/packages". | 808 // Special-case strings to support multi-level names like "myapp/packages". |
807 if (name is String) { | 809 if (name is String) { |
808 var path = join(dir, name); | 810 var path = join(dir, name); |
809 return exists(path).then((exists) { | 811 // Make sure errors propagate through future. |
810 if (!exists) { | 812 return defer(() { |
| 813 if (!entryExists(path)) { |
811 throw new ExpectException('File $name in $dir not found.'); | 814 throw new ExpectException('File $name in $dir not found.'); |
812 } | 815 } |
813 return validate(path); | 816 return validate(path); |
814 }); | 817 }); |
815 } | 818 } |
816 | 819 |
817 // TODO(nweiz): remove this when issue 4061 is fixed. | 820 // TODO(nweiz): remove this when issue 4061 is fixed. |
818 var stackTrace; | 821 var stackTrace; |
819 try { | 822 try { |
820 throw ""; | 823 throw ""; |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
866 /// tree before running a test, and for validating that the file system matches | 869 /// tree before running a test, and for validating that the file system matches |
867 /// some expectations after running it. | 870 /// some expectations after running it. |
868 class FileDescriptor extends Descriptor { | 871 class FileDescriptor extends Descriptor { |
869 /// The text contents of the file. | 872 /// The text contents of the file. |
870 final String contents; | 873 final String contents; |
871 | 874 |
872 FileDescriptor(Pattern name, this.contents) : super(name); | 875 FileDescriptor(Pattern name, this.contents) : super(name); |
873 | 876 |
874 /// Creates the file within [dir]. Returns a [Future] that is completed after | 877 /// Creates the file within [dir]. Returns a [Future] that is completed after |
875 /// the creation is done. | 878 /// the creation is done. |
876 Future<File> create(dir) { | 879 Future<File> create(dir) => |
877 return writeTextFile(join(dir, _stringName), contents); | 880 defer(() => writeTextFile(join(dir, _stringName), contents)); |
878 } | |
879 | 881 |
880 /// Deletes the file within [dir]. Returns a [Future] that is completed after | 882 /// Deletes the file within [dir]. Returns a [Future] that is completed after |
881 /// the deletion is done. | 883 /// the deletion is done. |
882 Future delete(dir) { | 884 Future delete(dir) => |
883 return deleteFile(join(dir, _stringName)); | 885 defer(() => deleteFile(join(dir, _stringName))); |
884 } | |
885 | 886 |
886 /// Validates that this file correctly matches the actual file at [path]. | 887 /// Validates that this file correctly matches the actual file at [path]. |
887 Future validate(String path) { | 888 Future validate(String path) { |
888 return _validateOneMatch(path, (file) { | 889 return _validateOneMatch(path, (file) { |
889 return readTextFile(file).then((text) { | 890 var text = readTextFile(file); |
890 if (text == contents) return null; | 891 if (text == contents) return null; |
891 | 892 |
892 throw new ExpectException( | 893 throw new ExpectException( |
893 'File $file should contain:\n\n$contents\n\n' | 894 'File $file should contain:\n\n$contents\n\n' |
894 'but contained:\n\n$text'); | 895 'but contained:\n\n$text'); |
895 }); | |
896 }); | 896 }); |
897 } | 897 } |
898 | 898 |
899 /// Loads the contents of the file. | 899 /// Loads the contents of the file. |
900 InputStream load(List<String> path) { | 900 InputStream load(List<String> path) { |
901 if (!path.isEmpty) { | 901 if (!path.isEmpty) { |
902 var joinedPath = Strings.join(path, '/'); | 902 var joinedPath = Strings.join(path, '/'); |
903 throw "Can't load $joinedPath from within $name: not a directory."; | 903 throw "Can't load $joinedPath from within $name: not a directory."; |
904 } | 904 } |
905 | 905 |
(...skipping 11 matching lines...) Expand all Loading... |
917 /// The files and directories contained in this directory. | 917 /// The files and directories contained in this directory. |
918 final List<Descriptor> contents; | 918 final List<Descriptor> contents; |
919 | 919 |
920 DirectoryDescriptor(Pattern name, List<Descriptor> contents) | 920 DirectoryDescriptor(Pattern name, List<Descriptor> contents) |
921 : this.contents = contents == null ? <Descriptor>[] : contents, | 921 : this.contents = contents == null ? <Descriptor>[] : contents, |
922 super(name); | 922 super(name); |
923 | 923 |
924 /// Creates the file within [dir]. Returns a [Future] that is completed after | 924 /// Creates the file within [dir]. Returns a [Future] that is completed after |
925 /// the creation is done. | 925 /// the creation is done. |
926 Future<Directory> create(parentDir) { | 926 Future<Directory> create(parentDir) { |
927 // Create the directory. | 927 // Make sure all errors propagate through the future. |
928 return ensureDir(join(parentDir, _stringName)).then((dir) { | 928 return defer(() { |
929 if (contents == null) return new Future<Directory>.immediate(dir); | 929 // Create the directory. |
| 930 var dir = ensureDir(join(parentDir, _stringName)); |
| 931 if (contents == null) return dir; |
930 | 932 |
931 // Recursively create all of its children. | 933 // Recursively create all of its children. |
932 final childFutures = | 934 var childFutures = contents.map((child) => child.create(dir)).toList(); |
933 contents.map((child) => child.create(dir)).toList(); | |
934 // Only complete once all of the children have been created too. | 935 // Only complete once all of the children have been created too. |
935 return Future.wait(childFutures).then((_) => dir); | 936 return Future.wait(childFutures).then((_) => dir); |
936 }); | 937 }); |
937 } | 938 } |
938 | 939 |
939 /// Deletes the directory within [dir]. Returns a [Future] that is completed | 940 /// Deletes the directory within [dir]. Returns a [Future] that is completed |
940 /// after the deletion is done. | 941 /// after the deletion is done. |
941 Future delete(dir) { | 942 Future delete(dir) { |
942 return deleteDir(join(dir, _stringName)); | 943 return deleteDir(join(dir, _stringName)); |
943 } | 944 } |
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1127 } | 1128 } |
1128 | 1129 |
1129 /// A descriptor that validates that no file exists with the given name. | 1130 /// A descriptor that validates that no file exists with the given name. |
1130 class NothingDescriptor extends Descriptor { | 1131 class NothingDescriptor extends Descriptor { |
1131 NothingDescriptor(String name) : super(name); | 1132 NothingDescriptor(String name) : super(name); |
1132 | 1133 |
1133 Future create(dir) => new Future.immediate(null); | 1134 Future create(dir) => new Future.immediate(null); |
1134 Future delete(dir) => new Future.immediate(null); | 1135 Future delete(dir) => new Future.immediate(null); |
1135 | 1136 |
1136 Future validate(String dir) { | 1137 Future validate(String dir) { |
1137 return exists(join(dir, name)).then((exists) { | 1138 // Make sure errors propagate through future. |
1138 if (exists) { | 1139 return defer(() { |
| 1140 if (entryExists(join(dir, name))) { |
1139 throw new ExpectException('File $name in $dir should not exist.'); | 1141 throw new ExpectException('File $name in $dir should not exist.'); |
1140 } | 1142 } |
1141 }); | 1143 }); |
1142 } | 1144 } |
1143 | 1145 |
1144 InputStream load(List<String> path) { | 1146 InputStream load(List<String> path) { |
1145 if (path.isEmpty) { | 1147 if (path.isEmpty) { |
1146 throw "Can't load the contents of $name: it doesn't exist."; | 1148 throw "Can't load the contents of $name: it doesn't exist."; |
1147 } else { | 1149 } else { |
1148 throw "Can't load ${Strings.join(path, '/')} from within $name: $name " | 1150 throw "Can't load ${Strings.join(path, '/')} from within $name: $name " |
1149 "doesn't exist."; | 1151 "doesn't exist."; |
1150 } | 1152 } |
1151 } | 1153 } |
1152 } | 1154 } |
1153 | 1155 |
1154 /// A function that creates a [Validator] subclass. | 1156 /// A function that creates a [Validator] subclass. |
1155 typedef Validator ValidatorCreator(Entrypoint entrypoint); | 1157 typedef Validator ValidatorCreator(Entrypoint entrypoint); |
1156 | 1158 |
1157 /// Schedules a single [Validator] to run on the [appPath]. Returns a scheduled | 1159 /// Schedules a single [Validator] to run on the [appPath]. Returns a scheduled |
1158 /// Future that contains the erros and warnings produced by that validator. | 1160 /// Future that contains the erros and warnings produced by that validator. |
1159 Future<Pair<List<String>, List<String>>> schedulePackageValidation( | 1161 Future<Pair<List<String>, List<String>>> schedulePackageValidation( |
1160 ValidatorCreator fn) { | 1162 ValidatorCreator fn) { |
1161 return _scheduleValue((sandboxDir) { | 1163 return _scheduleValue((sandboxDir) { |
1162 var cache = new SystemCache.withSources( | 1164 var cache = new SystemCache.withSources(join(sandboxDir, cachePath)); |
1163 join(sandboxDir, cachePath)); | |
1164 | 1165 |
1165 return Entrypoint.load(join(sandboxDir, appPath), cache) | 1166 return defer(() { |
1166 .then((entrypoint) { | 1167 var validator = fn(new Entrypoint(join(sandboxDir, appPath), cache)); |
1167 var validator = fn(entrypoint); | |
1168 return validator.validate().then((_) { | 1168 return validator.validate().then((_) { |
1169 return new Pair(validator.errors, validator.warnings); | 1169 return new Pair(validator.errors, validator.warnings); |
1170 }); | 1170 }); |
1171 }); | 1171 }); |
1172 }); | 1172 }); |
1173 } | 1173 } |
1174 | 1174 |
1175 /// A matcher that matches a Pair. | 1175 /// A matcher that matches a Pair. |
1176 Matcher pairOf(Matcher firstMatcher, Matcher lastMatcher) => | 1176 Matcher pairOf(Matcher firstMatcher, Matcher lastMatcher) => |
1177 new _PairMatcher(firstMatcher, lastMatcher); | 1177 new _PairMatcher(firstMatcher, lastMatcher); |
(...skipping 288 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1466 } | 1466 } |
1467 | 1467 |
1468 /// Ignore all requests with the given [method] and [path]. If one is | 1468 /// Ignore all requests with the given [method] and [path]. If one is |
1469 /// received, don't respond to it. | 1469 /// received, don't respond to it. |
1470 void ignore(String method, String path) => | 1470 void ignore(String method, String path) => |
1471 _ignored.add(new Pair(method, path)); | 1471 _ignored.add(new Pair(method, path)); |
1472 | 1472 |
1473 /// Raises an error complaining of an unexpected request. | 1473 /// Raises an error complaining of an unexpected request. |
1474 void _awaitHandle(HttpRequest request, HttpResponse response) { | 1474 void _awaitHandle(HttpRequest request, HttpResponse response) { |
1475 if (_ignored.contains(new Pair(request.method, request.path))) return; | 1475 if (_ignored.contains(new Pair(request.method, request.path))) return; |
1476 var future = timeout(new Future.immediate(null).then((_) { | 1476 // Make sure all errors propagate through future. |
| 1477 var future = timeout(defer(() { |
1477 if (_handlers.isEmpty) { | 1478 if (_handlers.isEmpty) { |
1478 fail('Unexpected ${request.method} request to ${request.path}.'); | 1479 fail('Unexpected ${request.method} request to ${request.path}.'); |
1479 } | 1480 } |
1480 return _handlers.removeFirst(); | 1481 return _handlers.removeFirst(); |
1481 }).then((handler) { | 1482 }).then((handler) { |
1482 handler(request, response); | 1483 handler(request, response); |
1483 }), _SCHEDULE_TIMEOUT, "waiting for a handler for ${request.method} " | 1484 }), _SCHEDULE_TIMEOUT, "waiting for a handler for ${request.method} " |
1484 "${request.path}"); | 1485 "${request.path}"); |
1485 expect(future, completes); | 1486 expect(future, completes); |
1486 } | 1487 } |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1550 /// calling [completion] is unnecessary. | 1551 /// calling [completion] is unnecessary. |
1551 void expectLater(Future actual, matcher, {String reason, | 1552 void expectLater(Future actual, matcher, {String reason, |
1552 FailureHandler failureHandler, bool verbose: false}) { | 1553 FailureHandler failureHandler, bool verbose: false}) { |
1553 _schedule((_) { | 1554 _schedule((_) { |
1554 return actual.then((value) { | 1555 return actual.then((value) { |
1555 expect(value, matcher, reason: reason, failureHandler: failureHandler, | 1556 expect(value, matcher, reason: reason, failureHandler: failureHandler, |
1556 verbose: false); | 1557 verbose: false); |
1557 }); | 1558 }); |
1558 }); | 1559 }); |
1559 } | 1560 } |
OLD | NEW |