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 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
108 var stream; | 108 var stream; |
109 try { | 109 try { |
110 stream = baseDir.load(path); | 110 stream = baseDir.load(path); |
111 } catch (e) { | 111 } catch (e) { |
112 response.statusCode = 404; | 112 response.statusCode = 404; |
113 response.contentLength = 0; | 113 response.contentLength = 0; |
114 response.outputStream.close(); | 114 response.outputStream.close(); |
115 return; | 115 return; |
116 } | 116 } |
117 | 117 |
118 var future = consumeInputStream(stream); | 118 stream.toBytes().then((data) { |
119 future.then((data) { | |
120 response.statusCode = 200; | 119 response.statusCode = 200; |
121 response.contentLength = data.length; | 120 response.contentLength = data.length; |
122 response.outputStream.write(data); | 121 response.outputStream.write(data); |
123 response.outputStream.close(); | 122 response.outputStream.close(); |
124 }).catchError((e) { | 123 }).catchError((e) { |
125 print("Exception while handling ${request.uri}: $e"); | 124 print("Exception while handling ${request.uri}: $e"); |
126 response.statusCode = 500; | 125 response.statusCode = 500; |
127 response.reasonPhrase = e.message; | 126 response.reasonPhrase = e.message; |
128 response.outputStream.close(); | 127 response.outputStream.close(); |
129 }); | 128 }); |
(...skipping 645 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
775 /// system entry within [dir]. Returns a [Future] that completes to `null` if | 774 /// system entry within [dir]. Returns a [Future] that completes to `null` if |
776 /// the entry is valid, or throws an error if it failed. | 775 /// the entry is valid, or throws an error if it failed. |
777 Future validate(String dir); | 776 Future validate(String dir); |
778 | 777 |
779 /// Deletes the file or directory within [dir]. Returns a [Future] that is | 778 /// Deletes the file or directory within [dir]. Returns a [Future] that is |
780 /// completed after the deletion is done. | 779 /// completed after the deletion is done. |
781 Future delete(String dir); | 780 Future delete(String dir); |
782 | 781 |
783 /// Loads the file at [path] from within this descriptor. If [path] is empty, | 782 /// Loads the file at [path] from within this descriptor. If [path] is empty, |
784 /// loads the contents of the descriptor itself. | 783 /// loads the contents of the descriptor itself. |
785 InputStream load(List<String> path); | 784 ByteStream load(List<String> path); |
786 | 785 |
787 /// Schedules the directory to be created before Pub is run with | 786 /// Schedules the directory to be created before Pub is run with |
788 /// [schedulePub]. The directory will be created relative to the sandbox | 787 /// [schedulePub]. The directory will be created relative to the sandbox |
789 /// directory. | 788 /// directory. |
790 // TODO(nweiz): Use implicit closurization once issue 2984 is fixed. | 789 // TODO(nweiz): Use implicit closurization once issue 2984 is fixed. |
791 void scheduleCreate() => _schedule((dir) => this.create(dir)); | 790 void scheduleCreate() => _schedule((dir) => this.create(dir)); |
792 | 791 |
793 /// Schedules the file or directory to be deleted recursively. | 792 /// Schedules the file or directory to be deleted recursively. |
794 void scheduleDelete() => _schedule((dir) => this.delete(dir)); | 793 void scheduleDelete() => _schedule((dir) => this.delete(dir)); |
795 | 794 |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
899 if (text == textContents) return null; | 898 if (text == textContents) return null; |
900 | 899 |
901 throw new ExpectException( | 900 throw new ExpectException( |
902 'File $file should contain:\n\n$textContents\n\n' | 901 'File $file should contain:\n\n$textContents\n\n' |
903 'but contained:\n\n$text'); | 902 'but contained:\n\n$text'); |
904 }); | 903 }); |
905 }); | 904 }); |
906 } | 905 } |
907 | 906 |
908 /// Loads the contents of the file. | 907 /// Loads the contents of the file. |
909 InputStream load(List<String> path) { | 908 ByteStream load(List<String> path) { |
910 if (!path.isEmpty) { | 909 if (!path.isEmpty) { |
911 var joinedPath = Strings.join(path, '/'); | 910 var joinedPath = Strings.join(path, '/'); |
912 throw "Can't load $joinedPath from within $name: not a directory."; | 911 throw "Can't load $joinedPath from within $name: not a directory."; |
913 } | 912 } |
914 | 913 |
915 var stream = new ListInputStream(); | 914 return new ByteStream.fromBytes(contents); |
916 stream.write(contents); | |
917 stream.markEndOfStream(); | |
918 return stream; | |
919 } | 915 } |
920 } | 916 } |
921 | 917 |
922 /// Describes a directory and its contents. These are used both for setting up | 918 /// Describes a directory and its contents. These are used both for setting up |
923 /// an expected directory tree before running a test, and for validating that | 919 /// an expected directory tree before running a test, and for validating that |
924 /// the file system matches some expectations after running it. | 920 /// the file system matches some expectations after running it. |
925 class DirectoryDescriptor extends Descriptor { | 921 class DirectoryDescriptor extends Descriptor { |
926 /// The files and directories contained in this directory. | 922 /// The files and directories contained in this directory. |
927 final List<Descriptor> contents; | 923 final List<Descriptor> contents; |
928 | 924 |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
960 // Validate each of the items in this directory. | 956 // Validate each of the items in this directory. |
961 final entryFutures = | 957 final entryFutures = |
962 contents.map((entry) => entry.validate(dir)).toList(); | 958 contents.map((entry) => entry.validate(dir)).toList(); |
963 | 959 |
964 // If they are all valid, the directory is valid. | 960 // If they are all valid, the directory is valid. |
965 return Future.wait(entryFutures).then((entries) => null); | 961 return Future.wait(entryFutures).then((entries) => null); |
966 }); | 962 }); |
967 } | 963 } |
968 | 964 |
969 /// Loads [path] from within this directory. | 965 /// Loads [path] from within this directory. |
970 InputStream load(List<String> path) { | 966 ByteStream load(List<String> path) { |
971 if (path.isEmpty) { | 967 if (path.isEmpty) { |
972 throw "Can't load the contents of $name: is a directory."; | 968 throw "Can't load the contents of $name: is a directory."; |
973 } | 969 } |
974 | 970 |
975 for (var descriptor in contents) { | 971 for (var descriptor in contents) { |
976 if (descriptor.name == path[0]) { | 972 if (descriptor.name == path[0]) { |
977 return descriptor.load(path.getRange(1, path.length - 1)); | 973 return descriptor.load(path.getRange(1, path.length - 1)); |
978 } | 974 } |
979 } | 975 } |
980 | 976 |
981 throw "Directory $name doesn't contain ${Strings.join(path, '/')}."; | 977 throw "Directory $name doesn't contain ${Strings.join(path, '/')}."; |
982 } | 978 } |
983 } | 979 } |
984 | 980 |
985 /// Wraps a [Future] that will complete to a [Descriptor] and makes it behave | 981 /// Wraps a [Future] that will complete to a [Descriptor] and makes it behave |
986 /// like a concrete [Descriptor]. This is necessary when the contents of the | 982 /// like a concrete [Descriptor]. This is necessary when the contents of the |
987 /// descriptor depends on information that's not available until part of the | 983 /// descriptor depends on information that's not available until part of the |
988 /// test run is completed. | 984 /// test run is completed. |
989 class FutureDescriptor extends Descriptor { | 985 class FutureDescriptor extends Descriptor { |
990 Future<Descriptor> _future; | 986 Future<Descriptor> _future; |
991 | 987 |
992 FutureDescriptor(this._future) : super('<unknown>'); | 988 FutureDescriptor(this._future) : super('<unknown>'); |
993 | 989 |
994 Future create(dir) => _future.then((desc) => desc.create(dir)); | 990 Future create(dir) => _future.then((desc) => desc.create(dir)); |
995 | 991 |
996 Future validate(dir) => _future.then((desc) => desc.validate(dir)); | 992 Future validate(dir) => _future.then((desc) => desc.validate(dir)); |
997 | 993 |
998 Future delete(dir) => _future.then((desc) => desc.delete(dir)); | 994 Future delete(dir) => _future.then((desc) => desc.delete(dir)); |
999 | 995 |
1000 InputStream load(List<String> path) { | 996 ByteStream load(List<String> path) { |
1001 var resultStream = new ListInputStream(); | 997 var controller = new StreamController<List<int>>(); |
1002 _future.then((desc) => pipeInputToInput(desc.load(path), resultStream)); | 998 _future.then((desc) => store(desc.load(path), controller)); |
1003 return resultStream; | 999 return new ByteStream(controller.stream); |
1004 } | 1000 } |
1005 } | 1001 } |
1006 | 1002 |
1007 /// Describes a Git repository and its contents. | 1003 /// Describes a Git repository and its contents. |
1008 class GitRepoDescriptor extends DirectoryDescriptor { | 1004 class GitRepoDescriptor extends DirectoryDescriptor { |
1009 GitRepoDescriptor(Pattern name, List<Descriptor> contents) | 1005 GitRepoDescriptor(Pattern name, List<Descriptor> contents) |
1010 : super(name, contents); | 1006 : super(name, contents); |
1011 | 1007 |
1012 /// Creates the Git repository and commits the contents. | 1008 /// Creates the Git repository and commits the contents. |
1013 Future<Directory> create(parentDir) { | 1009 Future<Directory> create(parentDir) { |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1086 | 1082 |
1087 /// Creates the files and directories within this tar file, then archives | 1083 /// Creates the files and directories within this tar file, then archives |
1088 /// them, compresses them, and saves the result to [parentDir]. | 1084 /// them, compresses them, and saves the result to [parentDir]. |
1089 Future<File> create(parentDir) { | 1085 Future<File> create(parentDir) { |
1090 // TODO(rnystrom): Use withTempDir(). | 1086 // TODO(rnystrom): Use withTempDir(). |
1091 var tempDir; | 1087 var tempDir; |
1092 return createTempDir().then((_tempDir) { | 1088 return createTempDir().then((_tempDir) { |
1093 tempDir = _tempDir; | 1089 tempDir = _tempDir; |
1094 return Future.wait(contents.map((child) => child.create(tempDir))); | 1090 return Future.wait(contents.map((child) => child.create(tempDir))); |
1095 }).then((createdContents) { | 1091 }).then((createdContents) { |
1096 return consumeInputStream(createTarGz(createdContents, baseDir: tempDir)); | 1092 return createTarGz(createdContents, baseDir: tempDir).toBytes(); |
1097 }).then((bytes) { | 1093 }).then((bytes) { |
1098 return new File(join(parentDir, _stringName)).writeAsBytes(bytes); | 1094 return new File(join(parentDir, _stringName)).writeAsBytes(bytes); |
1099 }).then((file) { | 1095 }).then((file) { |
1100 return deleteDir(tempDir).then((_) => file); | 1096 return deleteDir(tempDir).then((_) => file); |
1101 }); | 1097 }); |
1102 } | 1098 } |
1103 | 1099 |
1104 /// Validates that the `.tar.gz` file at [path] contains the expected | 1100 /// Validates that the `.tar.gz` file at [path] contains the expected |
1105 /// contents. | 1101 /// contents. |
1106 Future validate(String path) { | 1102 Future validate(String path) { |
1107 throw "TODO(nweiz): implement this"; | 1103 throw "TODO(nweiz): implement this"; |
1108 } | 1104 } |
1109 | 1105 |
1110 Future delete(dir) { | 1106 Future delete(dir) { |
1111 throw new UnsupportedError(''); | 1107 throw new UnsupportedError(''); |
1112 } | 1108 } |
1113 | 1109 |
1114 /// Loads the contents of this tar file. | 1110 /// Loads the contents of this tar file. |
1115 InputStream load(List<String> path) { | 1111 ByteStream load(List<String> path) { |
1116 if (!path.isEmpty) { | 1112 if (!path.isEmpty) { |
1117 var joinedPath = Strings.join(path, '/'); | 1113 var joinedPath = Strings.join(path, '/'); |
1118 throw "Can't load $joinedPath from within $name: not a directory."; | 1114 throw "Can't load $joinedPath from within $name: not a directory."; |
1119 } | 1115 } |
1120 | 1116 |
1121 var sinkStream = new ListInputStream(); | 1117 var controller = new StreamController<List<int>>(); |
1122 var tempDir; | 1118 var tempDir; |
1123 // TODO(rnystrom): Use withTempDir() here. | 1119 // TODO(rnystrom): Use withTempDir() here. |
1124 // TODO(nweiz): propagate any errors to the return value. See issue 3657. | 1120 // TODO(nweiz): propagate any errors to the return value. See issue 3657. |
1125 createTempDir().then((_tempDir) { | 1121 createTempDir().then((_tempDir) { |
1126 tempDir = _tempDir; | 1122 tempDir = _tempDir; |
1127 return create(tempDir); | 1123 return create(tempDir); |
1128 }).then((tar) { | 1124 }).then((tar) { |
1129 var sourceStream = tar.openInputStream(); | 1125 var sourceStream = tar.openInputStream(); |
1130 return pipeInputToInput(sourceStream, sinkStream).then((_) { | 1126 return store(wrapInputStream(sourceStream), controller).then((_) { |
1131 tempDir.delete(recursive: true); | 1127 tempDir.delete(recursive: true); |
1132 }); | 1128 }); |
1133 }); | 1129 }); |
1134 return sinkStream; | 1130 return new ByteStream(controller.stream); |
1135 } | 1131 } |
1136 } | 1132 } |
1137 | 1133 |
1138 /// A descriptor that validates that no file exists with the given name. | 1134 /// A descriptor that validates that no file exists with the given name. |
1139 class NothingDescriptor extends Descriptor { | 1135 class NothingDescriptor extends Descriptor { |
1140 NothingDescriptor(String name) : super(name); | 1136 NothingDescriptor(String name) : super(name); |
1141 | 1137 |
1142 Future create(dir) => new Future.immediate(null); | 1138 Future create(dir) => new Future.immediate(null); |
1143 Future delete(dir) => new Future.immediate(null); | 1139 Future delete(dir) => new Future.immediate(null); |
1144 | 1140 |
1145 Future validate(String dir) { | 1141 Future validate(String dir) { |
1146 return exists(join(dir, name)).then((exists) { | 1142 return exists(join(dir, name)).then((exists) { |
1147 if (exists) { | 1143 if (exists) { |
1148 throw new ExpectException('File $name in $dir should not exist.'); | 1144 throw new ExpectException('File $name in $dir should not exist.'); |
1149 } | 1145 } |
1150 }); | 1146 }); |
1151 } | 1147 } |
1152 | 1148 |
1153 InputStream load(List<String> path) { | 1149 ByteStream load(List<String> path) { |
1154 if (path.isEmpty) { | 1150 if (path.isEmpty) { |
1155 throw "Can't load the contents of $name: it doesn't exist."; | 1151 throw "Can't load the contents of $name: it doesn't exist."; |
1156 } else { | 1152 } else { |
1157 throw "Can't load ${Strings.join(path, '/')} from within $name: $name " | 1153 throw "Can't load ${Strings.join(path, '/')} from within $name: $name " |
1158 "doesn't exist."; | 1154 "doesn't exist."; |
1159 } | 1155 } |
1160 } | 1156 } |
1161 } | 1157 } |
1162 | 1158 |
1163 /// A function that creates a [Validator] subclass. | 1159 /// A function that creates a [Validator] subclass. |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1213 /// Before running the test, either [shouldExit] or [kill] must be called on | 1209 /// Before running the test, either [shouldExit] or [kill] must be called on |
1214 /// this to ensure that the process terminates when expected. | 1210 /// this to ensure that the process terminates when expected. |
1215 /// | 1211 /// |
1216 /// If the test fails, this will automatically print out any remaining stdout | 1212 /// If the test fails, this will automatically print out any remaining stdout |
1217 /// and stderr from the process to aid debugging. | 1213 /// and stderr from the process to aid debugging. |
1218 class ScheduledProcess { | 1214 class ScheduledProcess { |
1219 /// The name of the process. Used for error reporting. | 1215 /// The name of the process. Used for error reporting. |
1220 final String name; | 1216 final String name; |
1221 | 1217 |
1222 /// The process future that's scheduled to run. | 1218 /// The process future that's scheduled to run. |
1223 Future<Process> _processFuture; | 1219 Future<PubProcess> _processFuture; |
1224 | 1220 |
1225 /// The process that's scheduled to run. It may be null. | 1221 /// The process that's scheduled to run. It may be null. |
1226 Process _process; | 1222 Process _process; |
1227 | 1223 |
1228 /// The exit code of the scheduled program. It may be null. | 1224 /// The exit code of the scheduled program. It may be null. |
1229 int _exitCode; | 1225 int _exitCode; |
1230 | 1226 |
1231 /// A [StringInputStream] wrapping the stdout of the process that's scheduled | 1227 /// A future that will complete to a list of all the lines emitted on the |
1232 /// to run. | 1228 /// process's standard output stream. This is independent of what data is read |
1233 final Future<StringInputStream> _stdoutFuture; | 1229 /// from [_stdout]. |
| 1230 Future<List<String>> _stdoutLines; |
1234 | 1231 |
1235 /// A [StringInputStream] wrapping the stderr of the process that's scheduled | 1232 /// A [Stream] of stdout lines emitted by the process that's scheduled to run. |
1236 /// to run. | 1233 /// It may be null. |
1237 final Future<StringInputStream> _stderrFuture; | 1234 Stream<String> _stdout; |
| 1235 |
| 1236 /// A [Future] that will resolve to [_stdout] once it's available. |
| 1237 Future get _stdoutFuture => _processFuture.then((_) => _stdout); |
| 1238 |
| 1239 /// A [StreamSubscription] that controls [_stdout]. |
| 1240 StreamSubscription _stdoutSubscription; |
| 1241 |
| 1242 /// A future that will complete to a list of all the lines emitted on the |
| 1243 /// process's standard error stream. This is independent of what data is read |
| 1244 /// from [_stderr]. |
| 1245 Future<List<String>> _stderrLines; |
| 1246 |
| 1247 /// A [Stream] of stderr lines emitted by the process that's scheduled to run. |
| 1248 /// It may be null. |
| 1249 Stream<String> _stderr; |
| 1250 |
| 1251 /// A [Future] that will resolve to [_stderr] once it's available. |
| 1252 Future get _stderrFuture => _processFuture.then((_) => _stderr); |
| 1253 |
| 1254 /// A [StreamSubscription] that controls [_stderr]. |
| 1255 StreamSubscription _stderrSubscription; |
1238 | 1256 |
1239 /// The exit code of the process that's scheduled to run. This will naturally | 1257 /// The exit code of the process that's scheduled to run. This will naturally |
1240 /// only complete once the process has terminated. | 1258 /// only complete once the process has terminated. |
1241 Future<int> get _exitCodeFuture => _exitCodeCompleter.future; | 1259 Future<int> get _exitCodeFuture => _exitCodeCompleter.future; |
1242 | 1260 |
1243 /// The completer for [_exitCode]. | 1261 /// The completer for [_exitCode]. |
1244 final Completer<int> _exitCodeCompleter = new Completer(); | 1262 final Completer<int> _exitCodeCompleter = new Completer(); |
1245 | 1263 |
1246 /// Whether the user has scheduled the end of this process by calling either | 1264 /// Whether the user has scheduled the end of this process by calling either |
1247 /// [shouldExit] or [kill]. | 1265 /// [shouldExit] or [kill]. |
1248 bool _endScheduled = false; | 1266 bool _endScheduled = false; |
1249 | 1267 |
1250 /// Whether the process is expected to terminate at this point. | 1268 /// Whether the process is expected to terminate at this point. |
1251 bool _endExpected = false; | 1269 bool _endExpected = false; |
1252 | 1270 |
1253 /// Wraps a [Process] [Future] in a scheduled process. | 1271 /// Wraps a [Process] [Future] in a scheduled process. |
1254 ScheduledProcess(this.name, Future<Process> process) | 1272 ScheduledProcess(this.name, Future<PubProcess> process) |
1255 : _processFuture = process, | 1273 : _processFuture = process { |
1256 _stdoutFuture = process.then((p) => new StringInputStream(p.stdout)), | 1274 var pairFuture = process.then((p) { |
1257 _stderrFuture = process.then((p) => new StringInputStream(p.stderr)) { | |
1258 process.then((p) { | |
1259 _process = p; | 1275 _process = p; |
| 1276 |
| 1277 var stdoutTee = tee(p.stdout.handleError((e) { |
| 1278 registerException(e.error, e.stackTrace); |
| 1279 })); |
| 1280 var stdoutPair = streamWithSubscription(stdoutTee.last); |
| 1281 _stdout = stdoutPair.first; |
| 1282 _stdoutSubscription = stdoutPair.last; |
| 1283 |
| 1284 var stderrTee = tee(p.stderr.handleError((e) { |
| 1285 registerException(e.error, e.stackTrace); |
| 1286 })); |
| 1287 var stderrPair = streamWithSubscription(stderrTee.last); |
| 1288 _stderr = stderrPair.first; |
| 1289 _stderrSubscription = stderrPair.last; |
| 1290 |
| 1291 return new Pair(stdoutTee.first, stderrTee.first); |
1260 }); | 1292 }); |
1261 | 1293 |
| 1294 _stdoutLines = pairFuture.then((pair) => pair.first.toList()); |
| 1295 _stderrLines = pairFuture.then((pair) => pair.last.toList()); |
| 1296 |
1262 _schedule((_) { | 1297 _schedule((_) { |
1263 if (!_endScheduled) { | 1298 if (!_endScheduled) { |
1264 throw new StateError("Scheduled process $name must have shouldExit() " | 1299 throw new StateError("Scheduled process $name must have shouldExit() " |
1265 "or kill() called before the test is run."); | 1300 "or kill() called before the test is run."); |
1266 } | 1301 } |
1267 | 1302 |
1268 return process.then((p) { | 1303 return process.then((p) => p.exitCode).then((exitCode) { |
1269 p.onExit = (c) { | 1304 if (_endExpected) { |
| 1305 _exitCode = exitCode; |
| 1306 _exitCodeCompleter.complete(exitCode); |
| 1307 return; |
| 1308 } |
| 1309 |
| 1310 // Sleep for half a second in case _endExpected is set in the next |
| 1311 // scheduled event. |
| 1312 sleep(500).then((_) { |
1270 if (_endExpected) { | 1313 if (_endExpected) { |
1271 _exitCode = c; | 1314 _exitCodeCompleter.complete(exitCode); |
1272 _exitCodeCompleter.complete(c); | |
1273 return; | 1315 return; |
1274 } | 1316 } |
1275 | 1317 |
1276 // Sleep for half a second in case _endExpected is set in the next | 1318 return _printStreams().then((_) { |
1277 // scheduled event. | 1319 registerException(new ExpectException("Process $name ended " |
1278 sleep(500).then((_) { | 1320 "earlier than scheduled with exit code $exitCode")); |
1279 if (_endExpected) { | |
1280 _exitCodeCompleter.complete(c); | |
1281 return; | |
1282 } | |
1283 | |
1284 _printStreams().then((_) { | |
1285 registerException(new ExpectException("Process $name ended " | |
1286 "earlier than scheduled with exit code $c")); | |
1287 }); | |
1288 }); | 1321 }); |
1289 }; | 1322 }); |
1290 }); | 1323 }); |
1291 }); | 1324 }); |
1292 | 1325 |
1293 _scheduleOnException((_) { | 1326 _scheduleOnException((_) { |
1294 if (_process == null) return; | 1327 if (_process == null) return; |
1295 | 1328 |
1296 if (_exitCode == null) { | 1329 if (_exitCode == null) { |
1297 print("\nKilling process $name prematurely."); | 1330 print("\nKilling process $name prematurely."); |
1298 _endExpected = true; | 1331 _endExpected = true; |
1299 _process.kill(); | 1332 _process.kill(); |
1300 } | 1333 } |
1301 | 1334 |
1302 return _printStreams(); | 1335 return _printStreams(); |
1303 }); | 1336 }); |
1304 | 1337 |
1305 _scheduleCleanup((_) { | 1338 _scheduleCleanup((_) { |
1306 if (_process == null) return; | 1339 if (_process == null) return; |
1307 // Ensure that the process is dead and we aren't waiting on any IO. | 1340 // Ensure that the process is dead and we aren't waiting on any IO. |
1308 _process.kill(); | 1341 _process.kill(); |
1309 _process.stdout.close(); | 1342 _stdoutSubscription.cancel(); |
1310 _process.stderr.close(); | 1343 _stderrSubscription.cancel(); |
1311 }); | 1344 }); |
1312 } | 1345 } |
1313 | 1346 |
1314 /// Reads the next line of stdout from the process. | 1347 /// Reads the next line of stdout from the process. |
1315 Future<String> nextLine() { | 1348 Future<String> nextLine() { |
1316 return _scheduleValue((_) { | 1349 return _scheduleValue((_) { |
1317 return timeout(_stdoutFuture.then((stream) => readLine(stream)), | 1350 return timeout(_stdoutFuture.then((stream) => streamFirst(stream)), |
1318 _SCHEDULE_TIMEOUT, | 1351 _SCHEDULE_TIMEOUT, |
1319 "waiting for the next stdout line from process $name"); | 1352 "waiting for the next stdout line from process $name"); |
1320 }); | 1353 }); |
1321 } | 1354 } |
1322 | 1355 |
1323 /// Reads the next line of stderr from the process. | 1356 /// Reads the next line of stderr from the process. |
1324 Future<String> nextErrLine() { | 1357 Future<String> nextErrLine() { |
1325 return _scheduleValue((_) { | 1358 return _scheduleValue((_) { |
1326 return timeout(_stderrFuture.then((stream) => readLine(stream)), | 1359 return timeout(_stderrFuture.then((stream) => streamFirst(stream)), |
1327 _SCHEDULE_TIMEOUT, | 1360 _SCHEDULE_TIMEOUT, |
1328 "waiting for the next stderr line from process $name"); | 1361 "waiting for the next stderr line from process $name"); |
1329 }); | 1362 }); |
1330 } | 1363 } |
1331 | 1364 |
1332 /// Reads the remaining stdout from the process. This should only be called | 1365 /// Reads the remaining stdout from the process. This should only be called |
1333 /// after kill() or shouldExit(). | 1366 /// after kill() or shouldExit(). |
1334 Future<String> remainingStdout() { | 1367 Future<String> remainingStdout() { |
1335 if (!_endScheduled) { | 1368 if (!_endScheduled) { |
1336 throw new StateError("remainingStdout() should only be called after " | 1369 throw new StateError("remainingStdout() should only be called after " |
1337 "kill() or shouldExit()."); | 1370 "kill() or shouldExit()."); |
1338 } | 1371 } |
1339 | 1372 |
1340 return _scheduleValue((_) { | 1373 return _scheduleValue((_) { |
1341 return timeout(_stdoutFuture.then(consumeStringInputStream), | 1374 return timeout(_stdoutFuture.then((stream) => stream.toList()) |
| 1375 .then((lines) => lines.join("\n")), |
1342 _SCHEDULE_TIMEOUT, | 1376 _SCHEDULE_TIMEOUT, |
1343 "waiting for the last stdout line from process $name"); | 1377 "waiting for the last stdout line from process $name"); |
1344 }); | 1378 }); |
1345 } | 1379 } |
1346 | 1380 |
1347 /// Reads the remaining stderr from the process. This should only be called | 1381 /// Reads the remaining stderr from the process. This should only be called |
1348 /// after kill() or shouldExit(). | 1382 /// after kill() or shouldExit(). |
1349 Future<String> remainingStderr() { | 1383 Future<String> remainingStderr() { |
1350 if (!_endScheduled) { | 1384 if (!_endScheduled) { |
1351 throw new StateError("remainingStderr() should only be called after " | 1385 throw new StateError("remainingStderr() should only be called after " |
1352 "kill() or shouldExit()."); | 1386 "kill() or shouldExit()."); |
1353 } | 1387 } |
1354 | 1388 |
1355 return _scheduleValue((_) { | 1389 return _scheduleValue((_) { |
1356 return timeout(_stderrFuture.then(consumeStringInputStream), | 1390 return timeout(_stderrFuture.then((stream) => stream.toList()) |
| 1391 .then((lines) => lines.join("\n")), |
1357 _SCHEDULE_TIMEOUT, | 1392 _SCHEDULE_TIMEOUT, |
1358 "waiting for the last stderr line from process $name"); | 1393 "waiting for the last stderr line from process $name"); |
1359 }); | 1394 }); |
1360 } | 1395 } |
1361 | 1396 |
1362 /// Writes [line] to the process as stdin. | 1397 /// Writes [line] to the process as stdin. |
1363 void writeLine(String line) { | 1398 void writeLine(String line) { |
1364 _schedule((_) => _processFuture.then( | 1399 _schedule((_) => _processFuture.then( |
1365 (p) => p.stdin.writeString('$line\n'))); | 1400 (p) => p.stdin.write('$line\n'.charCodes))); |
1366 } | 1401 } |
1367 | 1402 |
1368 /// Kills the process, and waits until it's dead. | 1403 /// Kills the process, and waits until it's dead. |
1369 void kill() { | 1404 void kill() { |
1370 _endScheduled = true; | 1405 _endScheduled = true; |
1371 _schedule((_) { | 1406 _schedule((_) { |
1372 _endExpected = true; | 1407 _endExpected = true; |
1373 _process.kill(); | 1408 _process.kill(); |
1374 timeout(_exitCodeCompleter.future, _SCHEDULE_TIMEOUT, | 1409 timeout(_exitCodeFuture, _SCHEDULE_TIMEOUT, |
1375 "waiting for process $name to die"); | 1410 "waiting for process $name to die"); |
1376 }); | 1411 }); |
1377 } | 1412 } |
1378 | 1413 |
1379 /// Waits for the process to exit, and verifies that the exit code matches | 1414 /// Waits for the process to exit, and verifies that the exit code matches |
1380 /// [expectedExitCode] (if given). | 1415 /// [expectedExitCode] (if given). |
1381 void shouldExit([int expectedExitCode]) { | 1416 void shouldExit([int expectedExitCode]) { |
1382 _endScheduled = true; | 1417 _endScheduled = true; |
1383 _schedule((_) { | 1418 _schedule((_) { |
1384 _endExpected = true; | 1419 _endExpected = true; |
1385 return timeout(_exitCodeCompleter.future, _SCHEDULE_TIMEOUT, | 1420 return timeout(_exitCodeFuture, _SCHEDULE_TIMEOUT, |
1386 "waiting for process $name to exit").then((exitCode) { | 1421 "waiting for process $name to exit").then((exitCode) { |
1387 if (expectedExitCode != null) { | 1422 if (expectedExitCode != null) { |
1388 expect(exitCode, equals(expectedExitCode)); | 1423 expect(exitCode, equals(expectedExitCode)); |
1389 } | 1424 } |
1390 }); | 1425 }); |
1391 }); | 1426 }); |
1392 } | 1427 } |
1393 | 1428 |
1394 /// Prints the remaining data in the process's stdout and stderr streams. | 1429 /// Prints the remaining data in the process's stdout and stderr streams. |
1395 /// Prints nothing if the straems are empty. | 1430 /// Prints nothing if the streams are empty. |
1396 Future _printStreams() { | 1431 Future _printStreams() { |
1397 Future printStream(String streamName, StringInputStream stream) { | 1432 void printStream(String streamName, List<String> lines) { |
1398 return consumeStringInputStream(stream).then((output) { | 1433 if (lines.isEmpty) return; |
1399 if (output.isEmpty) return; | |
1400 | 1434 |
1401 print('\nProcess $name $streamName:'); | 1435 print('\nProcess $name $streamName:'); |
1402 for (var line in output.trim().split("\n")) { | 1436 for (var line in lines) { |
1403 print('| $line'); | 1437 print('| $line'); |
1404 } | 1438 } |
1405 return; | |
1406 }); | |
1407 } | 1439 } |
1408 | 1440 |
1409 return _stdoutFuture.then((stdout) { | 1441 return _stdoutLines.then((stdoutLines) { |
1410 return _stderrFuture.then((stderr) { | 1442 printStream('stdout', stdoutLines); |
1411 return printStream('stdout', stdout) | 1443 return _stderrLines.then((stderrLines) { |
1412 .then((_) => printStream('stderr', stderr)); | 1444 printStream('stderr', stderrLines); |
1413 }); | 1445 }); |
1414 }); | 1446 }); |
1415 } | 1447 } |
1416 } | 1448 } |
1417 | 1449 |
1418 /// A class representing an [HttpServer] that's scheduled to run in the course | 1450 /// A class representing an [HttpServer] that's scheduled to run in the course |
1419 /// of the test. This class allows the server's request handling to be | 1451 /// of the test. This class allows the server's request handling to be |
1420 /// scheduled synchronously. All operations on this class are scheduled. | 1452 /// scheduled synchronously. All operations on this class are scheduled. |
1421 class ScheduledServer { | 1453 class ScheduledServer { |
1422 /// The wrapped server. | 1454 /// The wrapped server. |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1559 /// calling [completion] is unnecessary. | 1591 /// calling [completion] is unnecessary. |
1560 void expectLater(Future actual, matcher, {String reason, | 1592 void expectLater(Future actual, matcher, {String reason, |
1561 FailureHandler failureHandler, bool verbose: false}) { | 1593 FailureHandler failureHandler, bool verbose: false}) { |
1562 _schedule((_) { | 1594 _schedule((_) { |
1563 return actual.then((value) { | 1595 return actual.then((value) { |
1564 expect(value, matcher, reason: reason, failureHandler: failureHandler, | 1596 expect(value, matcher, reason: reason, failureHandler: failureHandler, |
1565 verbose: false); | 1597 verbose: false); |
1566 }); | 1598 }); |
1567 }); | 1599 }); |
1568 } | 1600 } |
OLD | NEW |