Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(15)

Side by Side Diff: utils/tests/pub/test_pub.dart

Issue 12021008: Use the dart:async Stream API thoroughly in Pub. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Code review changes Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « utils/tests/pub/pub_uploader_test.dart ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
88 var stream; 88 var stream;
89 try { 89 try {
90 stream = baseDir.load(path); 90 stream = baseDir.load(path);
91 } catch (e) { 91 } catch (e) {
92 response.statusCode = 404; 92 response.statusCode = 404;
93 response.contentLength = 0; 93 response.contentLength = 0;
94 response.outputStream.close(); 94 response.outputStream.close();
95 return; 95 return;
96 } 96 }
97 97
98 var future = consumeInputStream(stream); 98 stream.toBytes().then((data) {
99 future.then((data) {
100 response.statusCode = 200; 99 response.statusCode = 200;
101 response.contentLength = data.length; 100 response.contentLength = data.length;
102 response.outputStream.write(data); 101 response.outputStream.write(data);
103 response.outputStream.close(); 102 response.outputStream.close();
104 }).catchError((e) { 103 }).catchError((e) {
105 print("Exception while handling ${request.uri}: $e"); 104 print("Exception while handling ${request.uri}: $e");
106 response.statusCode = 500; 105 response.statusCode = 500;
107 response.reasonPhrase = e.message; 106 response.reasonPhrase = e.message;
108 response.outputStream.close(); 107 response.outputStream.close();
109 }); 108 });
(...skipping 648 matching lines...) Expand 10 before | Expand all | Expand 10 after
758 /// system entry within [dir]. Returns a [Future] that completes to `null` if 757 /// system entry within [dir]. Returns a [Future] that completes to `null` if
759 /// the entry is valid, or throws an error if it failed. 758 /// the entry is valid, or throws an error if it failed.
760 Future validate(String dir); 759 Future validate(String dir);
761 760
762 /// Deletes the file or directory within [dir]. Returns a [Future] that is 761 /// Deletes the file or directory within [dir]. Returns a [Future] that is
763 /// completed after the deletion is done. 762 /// completed after the deletion is done.
764 Future delete(String dir); 763 Future delete(String dir);
765 764
766 /// Loads the file at [path] from within this descriptor. If [path] is empty, 765 /// Loads the file at [path] from within this descriptor. If [path] is empty,
767 /// loads the contents of the descriptor itself. 766 /// loads the contents of the descriptor itself.
768 InputStream load(List<String> path); 767 ByteStream load(List<String> path);
769 768
770 /// Schedules the directory to be created before Pub is run with [runPub]. 769 /// Schedules the directory to be created before Pub is run with [runPub].
771 /// The directory will be created relative to the sandbox directory. 770 /// The directory will be created relative to the sandbox directory.
772 // TODO(nweiz): Use implicit closurization once issue 2984 is fixed. 771 // TODO(nweiz): Use implicit closurization once issue 2984 is fixed.
773 void scheduleCreate() => _schedule((dir) => this.create(dir)); 772 void scheduleCreate() => _schedule((dir) => this.create(dir));
774 773
775 /// Schedules the file or directory to be deleted recursively. 774 /// Schedules the file or directory to be deleted recursively.
776 void scheduleDelete() => _schedule((dir) => this.delete(dir)); 775 void scheduleDelete() => _schedule((dir) => this.delete(dir));
777 776
778 /// Schedules the directory to be validated after Pub is run with [runPub]. 777 /// Schedules the directory to be validated after Pub is run with [runPub].
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
873 return readTextFile(file).then((text) { 872 return readTextFile(file).then((text) {
874 if (text == contents) return null; 873 if (text == contents) return null;
875 874
876 Expect.fail('File $file should contain:\n\n$contents\n\n' 875 Expect.fail('File $file should contain:\n\n$contents\n\n'
877 'but contained:\n\n$text'); 876 'but contained:\n\n$text');
878 }); 877 });
879 }); 878 });
880 } 879 }
881 880
882 /// Loads the contents of the file. 881 /// Loads the contents of the file.
883 InputStream load(List<String> path) { 882 ByteStream load(List<String> path) {
884 if (!path.isEmpty) { 883 if (!path.isEmpty) {
885 var joinedPath = Strings.join(path, '/'); 884 var joinedPath = Strings.join(path, '/');
886 throw "Can't load $joinedPath from within $name: not a directory."; 885 throw "Can't load $joinedPath from within $name: not a directory.";
887 } 886 }
888 887
889 var stream = new ListInputStream(); 888 return new ByteStream.fromBytes(contents.charCodes);
890 stream.write(contents.charCodes);
891 stream.markEndOfStream();
892 return stream;
893 } 889 }
894 } 890 }
895 891
896 /// Describes a directory and its contents. These are used both for setting up 892 /// Describes a directory and its contents. These are used both for setting up
897 /// an expected directory tree before running a test, and for validating that 893 /// an expected directory tree before running a test, and for validating that
898 /// the file system matches some expectations after running it. 894 /// the file system matches some expectations after running it.
899 class DirectoryDescriptor extends Descriptor { 895 class DirectoryDescriptor extends Descriptor {
900 /// The files and directories contained in this directory. 896 /// The files and directories contained in this directory.
901 final List<Descriptor> contents; 897 final List<Descriptor> contents;
902 898
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
934 // Validate each of the items in this directory. 930 // Validate each of the items in this directory.
935 final entryFutures = 931 final entryFutures =
936 contents.mappedBy((entry) => entry.validate(dir)).toList(); 932 contents.mappedBy((entry) => entry.validate(dir)).toList();
937 933
938 // If they are all valid, the directory is valid. 934 // If they are all valid, the directory is valid.
939 return Future.wait(entryFutures).then((entries) => null); 935 return Future.wait(entryFutures).then((entries) => null);
940 }); 936 });
941 } 937 }
942 938
943 /// Loads [path] from within this directory. 939 /// Loads [path] from within this directory.
944 InputStream load(List<String> path) { 940 ByteStream load(List<String> path) {
945 if (path.isEmpty) { 941 if (path.isEmpty) {
946 throw "Can't load the contents of $name: is a directory."; 942 throw "Can't load the contents of $name: is a directory.";
947 } 943 }
948 944
949 for (var descriptor in contents) { 945 for (var descriptor in contents) {
950 if (descriptor.name == path[0]) { 946 if (descriptor.name == path[0]) {
951 return descriptor.load(path.getRange(1, path.length - 1)); 947 return descriptor.load(path.getRange(1, path.length - 1));
952 } 948 }
953 } 949 }
954 950
955 throw "Directory $name doesn't contain ${Strings.join(path, '/')}."; 951 throw "Directory $name doesn't contain ${Strings.join(path, '/')}.";
956 } 952 }
957 } 953 }
958 954
959 /// Wraps a [Future] that will complete to a [Descriptor] and makes it behave 955 /// Wraps a [Future] that will complete to a [Descriptor] and makes it behave
960 /// like a concrete [Descriptor]. This is necessary when the contents of the 956 /// like a concrete [Descriptor]. This is necessary when the contents of the
961 /// descriptor depends on information that's not available until part of the 957 /// descriptor depends on information that's not available until part of the
962 /// test run is completed. 958 /// test run is completed.
963 class FutureDescriptor extends Descriptor { 959 class FutureDescriptor extends Descriptor {
964 Future<Descriptor> _future; 960 Future<Descriptor> _future;
965 961
966 FutureDescriptor(this._future) : super('<unknown>'); 962 FutureDescriptor(this._future) : super('<unknown>');
967 963
968 Future create(dir) => _future.then((desc) => desc.create(dir)); 964 Future create(dir) => _future.then((desc) => desc.create(dir));
969 965
970 Future validate(dir) => _future.then((desc) => desc.validate(dir)); 966 Future validate(dir) => _future.then((desc) => desc.validate(dir));
971 967
972 Future delete(dir) => _future.then((desc) => desc.delete(dir)); 968 Future delete(dir) => _future.then((desc) => desc.delete(dir));
973 969
974 InputStream load(List<String> path) { 970 ByteStream load(List<String> path) {
975 var resultStream = new ListInputStream(); 971 var controller = new StreamController<List<int>>();
976 _future.then((desc) => pipeInputToInput(desc.load(path), resultStream)); 972 _future.then((desc) => store(desc.load(path), controller));
977 return resultStream; 973 return new ByteStream(controller.stream);
978 } 974 }
979 } 975 }
980 976
981 /// Describes a Git repository and its contents. 977 /// Describes a Git repository and its contents.
982 class GitRepoDescriptor extends DirectoryDescriptor { 978 class GitRepoDescriptor extends DirectoryDescriptor {
983 GitRepoDescriptor(Pattern name, List<Descriptor> contents) 979 GitRepoDescriptor(Pattern name, List<Descriptor> contents)
984 : super(name, contents); 980 : super(name, contents);
985 981
986 /// Creates the Git repository and commits the contents. 982 /// Creates the Git repository and commits the contents.
987 Future<Directory> create(parentDir) { 983 Future<Directory> create(parentDir) {
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
1067 1063
1068 /// Creates the files and directories within this tar file, then archives 1064 /// Creates the files and directories within this tar file, then archives
1069 /// them, compresses them, and saves the result to [parentDir]. 1065 /// them, compresses them, and saves the result to [parentDir].
1070 Future<File> create(parentDir) { 1066 Future<File> create(parentDir) {
1071 // TODO(rnystrom): Use withTempDir(). 1067 // TODO(rnystrom): Use withTempDir().
1072 var tempDir; 1068 var tempDir;
1073 return createTempDir().then((_tempDir) { 1069 return createTempDir().then((_tempDir) {
1074 tempDir = _tempDir; 1070 tempDir = _tempDir;
1075 return Future.wait(contents.mappedBy((child) => child.create(tempDir))); 1071 return Future.wait(contents.mappedBy((child) => child.create(tempDir)));
1076 }).then((createdContents) { 1072 }).then((createdContents) {
1077 return consumeInputStream(createTarGz(createdContents, baseDir: tempDir)); 1073 return createTarGz(createdContents, baseDir: tempDir).toBytes();
1078 }).then((bytes) { 1074 }).then((bytes) {
1079 return new File(join(parentDir, _stringName)).writeAsBytes(bytes); 1075 return new File(join(parentDir, _stringName)).writeAsBytes(bytes);
1080 }).then((file) { 1076 }).then((file) {
1081 return deleteDir(tempDir).then((_) => file); 1077 return deleteDir(tempDir).then((_) => file);
1082 }); 1078 });
1083 } 1079 }
1084 1080
1085 /// Validates that the `.tar.gz` file at [path] contains the expected 1081 /// Validates that the `.tar.gz` file at [path] contains the expected
1086 /// contents. 1082 /// contents.
1087 Future validate(String path) { 1083 Future validate(String path) {
1088 throw "TODO(nweiz): implement this"; 1084 throw "TODO(nweiz): implement this";
1089 } 1085 }
1090 1086
1091 Future delete(dir) { 1087 Future delete(dir) {
1092 throw new UnsupportedError(''); 1088 throw new UnsupportedError('');
1093 } 1089 }
1094 1090
1095 /// Loads the contents of this tar file. 1091 /// Loads the contents of this tar file.
1096 InputStream load(List<String> path) { 1092 ByteStream load(List<String> path) {
1097 if (!path.isEmpty) { 1093 if (!path.isEmpty) {
1098 var joinedPath = Strings.join(path, '/'); 1094 var joinedPath = Strings.join(path, '/');
1099 throw "Can't load $joinedPath from within $name: not a directory."; 1095 throw "Can't load $joinedPath from within $name: not a directory.";
1100 } 1096 }
1101 1097
1102 var sinkStream = new ListInputStream(); 1098 var controller = new StreamController<List<int>>();
1103 var tempDir; 1099 var tempDir;
1104 // TODO(rnystrom): Use withTempDir() here. 1100 // TODO(rnystrom): Use withTempDir() here.
1105 // TODO(nweiz): propagate any errors to the return value. See issue 3657. 1101 // TODO(nweiz): propagate any errors to the return value. See issue 3657.
1106 createTempDir().then((_tempDir) { 1102 createTempDir().then((_tempDir) {
1107 tempDir = _tempDir; 1103 tempDir = _tempDir;
1108 return create(tempDir); 1104 return create(tempDir);
1109 }).then((tar) { 1105 }).then((tar) {
1110 var sourceStream = tar.openInputStream(); 1106 var sourceStream = tar.openInputStream();
1111 return pipeInputToInput(sourceStream, sinkStream).then((_) { 1107 return store(wrapInputStream(sourceStream), controller).then((_) {
1112 tempDir.delete(recursive: true); 1108 tempDir.delete(recursive: true);
1113 }); 1109 });
1114 }); 1110 });
1115 return sinkStream; 1111 return new ByteStream(controller.stream);
1116 } 1112 }
1117 } 1113 }
1118 1114
1119 /// A descriptor that validates that no file exists with the given name. 1115 /// A descriptor that validates that no file exists with the given name.
1120 class NothingDescriptor extends Descriptor { 1116 class NothingDescriptor extends Descriptor {
1121 NothingDescriptor(String name) : super(name); 1117 NothingDescriptor(String name) : super(name);
1122 1118
1123 Future create(dir) => new Future.immediate(null); 1119 Future create(dir) => new Future.immediate(null);
1124 Future delete(dir) => new Future.immediate(null); 1120 Future delete(dir) => new Future.immediate(null);
1125 1121
1126 Future validate(String dir) { 1122 Future validate(String dir) {
1127 return exists(join(dir, name)).then((exists) { 1123 return exists(join(dir, name)).then((exists) {
1128 if (exists) Expect.fail('File $name in $dir should not exist.'); 1124 if (exists) Expect.fail('File $name in $dir should not exist.');
1129 }); 1125 });
1130 } 1126 }
1131 1127
1132 InputStream load(List<String> path) { 1128 ByteStream load(List<String> path) {
1133 if (path.isEmpty) { 1129 if (path.isEmpty) {
1134 throw "Can't load the contents of $name: it doesn't exist."; 1130 throw "Can't load the contents of $name: it doesn't exist.";
1135 } else { 1131 } else {
1136 throw "Can't load ${Strings.join(path, '/')} from within $name: $name " 1132 throw "Can't load ${Strings.join(path, '/')} from within $name: $name "
1137 "doesn't exist."; 1133 "doesn't exist.";
1138 } 1134 }
1139 } 1135 }
1140 } 1136 }
1141 1137
1142 /// A function that creates a [Validator] subclass. 1138 /// A function that creates a [Validator] subclass.
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
1193 /// Before running the test, either [shouldExit] or [kill] must be called on 1189 /// Before running the test, either [shouldExit] or [kill] must be called on
1194 /// this to ensure that the process terminates when expected. 1190 /// this to ensure that the process terminates when expected.
1195 /// 1191 ///
1196 /// If the test fails, this will automatically print out any remaining stdout 1192 /// If the test fails, this will automatically print out any remaining stdout
1197 /// and stderr from the process to aid debugging. 1193 /// and stderr from the process to aid debugging.
1198 class ScheduledProcess { 1194 class ScheduledProcess {
1199 /// The name of the process. Used for error reporting. 1195 /// The name of the process. Used for error reporting.
1200 final String name; 1196 final String name;
1201 1197
1202 /// The process future that's scheduled to run. 1198 /// The process future that's scheduled to run.
1203 Future<Process> _processFuture; 1199 Future<PubProcess> _processFuture;
1204 1200
1205 /// The process that's scheduled to run. It may be null. 1201 /// The process that's scheduled to run. It may be null.
1206 Process _process; 1202 Process _process;
1207 1203
1208 /// The exit code of the scheduled program. It may be null. 1204 /// The exit code of the scheduled program. It may be null.
1209 int _exitCode; 1205 int _exitCode;
1210 1206
1211 /// A [StringInputStream] wrapping the stdout of the process that's scheduled 1207 /// A future that will complete to a list of all the lines emitted on the
1212 /// to run. 1208 /// process's standard output stream. This is independent of what data is read
1213 final Future<StringInputStream> _stdoutFuture; 1209 /// from [_stdout].
1210 Future<List<String>> _stdoutLines;
1214 1211
1215 /// A [StringInputStream] wrapping the stderr of the process that's scheduled 1212 /// A [Stream] of stdout lines emitted by the process that's scheduled to run.
1216 /// to run. 1213 /// It may be null.
1217 final Future<StringInputStream> _stderrFuture; 1214 Stream<String> _stdout;
1215
1216 /// A [Future] that will resolve to [_stdout] once it's available.
1217 Future get _stdoutFuture => _processFuture.then((_) => _stdout);
1218
1219 /// A [StreamSubscription] that controls [_stdout].
1220 StreamSubscription _stdoutSubscription;
1221
1222 /// A future that will complete to a list of all the lines emitted on the
1223 /// process's standard error stream. This is independent of what data is read
1224 /// from [_stderr].
1225 Future<List<String>> _stderrLines;
1226
1227 /// A [Stream] of stderr lines emitted by the process that's scheduled to run.
1228 /// It may be null.
1229 Stream<String> _stderr;
1230
1231 /// A [Future] that will resolve to [_stderr] once it's available.
1232 Future get _stderrFuture => _processFuture.then((_) => _stderr);
1233
1234 /// A [StreamSubscription] that controls [_stderr].
1235 StreamSubscription _stderrSubscription;
1218 1236
1219 /// The exit code of the process that's scheduled to run. This will naturally 1237 /// The exit code of the process that's scheduled to run. This will naturally
1220 /// only complete once the process has terminated. 1238 /// only complete once the process has terminated.
1221 Future<int> get _exitCodeFuture => _exitCodeCompleter.future; 1239 Future<int> get _exitCodeFuture => _exitCodeCompleter.future;
1222 1240
1223 /// The completer for [_exitCode]. 1241 /// The completer for [_exitCode].
1224 final Completer<int> _exitCodeCompleter = new Completer(); 1242 final Completer<int> _exitCodeCompleter = new Completer();
1225 1243
1226 /// Whether the user has scheduled the end of this process by calling either 1244 /// Whether the user has scheduled the end of this process by calling either
1227 /// [shouldExit] or [kill]. 1245 /// [shouldExit] or [kill].
1228 bool _endScheduled = false; 1246 bool _endScheduled = false;
1229 1247
1230 /// Whether the process is expected to terminate at this point. 1248 /// Whether the process is expected to terminate at this point.
1231 bool _endExpected = false; 1249 bool _endExpected = false;
1232 1250
1233 /// Wraps a [Process] [Future] in a scheduled process. 1251 /// Wraps a [Process] [Future] in a scheduled process.
1234 ScheduledProcess(this.name, Future<Process> process) 1252 ScheduledProcess(this.name, Future<PubProcess> process)
1235 : _processFuture = process, 1253 : _processFuture = process {
1236 _stdoutFuture = process.then((p) => new StringInputStream(p.stdout)), 1254 var pairFuture = process.then((p) {
1237 _stderrFuture = process.then((p) => new StringInputStream(p.stderr)) {
1238 process.then((p) {
1239 _process = p; 1255 _process = p;
1256
1257 var stdoutTee = tee(p.stdout.handleError((e) {
1258 registerException(e.error, e.stackTrace);
1259 }));
1260 var stdoutPair = streamWithSubscription(stdoutTee.last);
1261 _stdout = stdoutPair.first;
1262 _stdoutSubscription = stdoutPair.last;
1263
1264 var stderrTee = tee(p.stderr.handleError((e) {
1265 registerException(e.error, e.stackTrace);
1266 }));
1267 var stderrPair = streamWithSubscription(stderrTee.last);
1268 _stderr = stderrPair.first;
1269 _stderrSubscription = stderrPair.last;
1270
1271 return new Pair(stdoutTee.first, stderrTee.first);
1240 }); 1272 });
1241 1273
1274 _stdoutLines = pairFuture.then((pair) => pair.first.toList());
1275 _stderrLines = pairFuture.then((pair) => pair.last.toList());
1276
1242 _schedule((_) { 1277 _schedule((_) {
1243 if (!_endScheduled) { 1278 if (!_endScheduled) {
1244 throw new StateError("Scheduled process $name must have shouldExit() " 1279 throw new StateError("Scheduled process $name must have shouldExit() "
1245 "or kill() called before the test is run."); 1280 "or kill() called before the test is run.");
1246 } 1281 }
1247 1282
1248 return process.then((p) { 1283 return process.then((p) => p.exitCode).then((exitCode) {
1249 p.onExit = (c) { 1284 if (_endExpected) {
1285 _exitCode = exitCode;
1286 _exitCodeCompleter.complete(exitCode);
1287 return;
1288 }
1289
1290 // Sleep for half a second in case _endExpected is set in the next
1291 // scheduled event.
1292 sleep(500).then((_) {
1250 if (_endExpected) { 1293 if (_endExpected) {
1251 _exitCode = c; 1294 _exitCodeCompleter.complete(exitCode);
1252 _exitCodeCompleter.complete(c);
1253 return; 1295 return;
1254 } 1296 }
1255 1297
1256 // Sleep for half a second in case _endExpected is set in the next 1298 return _printStreams().then((_) {
1257 // scheduled event. 1299 registerException(new ExpectException("Process $name ended "
1258 sleep(500).then((_) { 1300 "earlier than scheduled with exit code $exitCode"));
1259 if (_endExpected) {
1260 _exitCodeCompleter.complete(c);
1261 return;
1262 }
1263
1264 _printStreams().then((_) {
1265 registerException(new ExpectException("Process $name ended "
1266 "earlier than scheduled with exit code $c"));
1267 });
1268 }); 1301 });
1269 }; 1302 });
1270 }); 1303 });
1271 }); 1304 });
1272 1305
1273 _scheduleOnException((_) { 1306 _scheduleOnException((_) {
1274 if (_process == null) return; 1307 if (_process == null) return;
1275 1308
1276 if (_exitCode == null) { 1309 if (_exitCode == null) {
1277 print("\nKilling process $name prematurely."); 1310 print("\nKilling process $name prematurely.");
1278 _endExpected = true; 1311 _endExpected = true;
1279 _process.kill(); 1312 _process.kill();
1280 } 1313 }
1281 1314
1282 return _printStreams(); 1315 return _printStreams();
1283 }); 1316 });
1284 1317
1285 _scheduleCleanup((_) { 1318 _scheduleCleanup((_) {
1286 if (_process == null) return; 1319 if (_process == null) return;
1287 // Ensure that the process is dead and we aren't waiting on any IO. 1320 // Ensure that the process is dead and we aren't waiting on any IO.
1288 _process.kill(); 1321 _process.kill();
1289 _process.stdout.close(); 1322 _stdoutSubscription.cancel();
1290 _process.stderr.close(); 1323 _stderrSubscription.cancel();
1291 }); 1324 });
1292 } 1325 }
1293 1326
1294 /// Reads the next line of stdout from the process. 1327 /// Reads the next line of stdout from the process.
1295 Future<String> nextLine() { 1328 Future<String> nextLine() {
1296 return _scheduleValue((_) { 1329 return _scheduleValue((_) {
1297 return timeout(_stdoutFuture.then((stream) => readLine(stream)), 1330 return timeout(_stdoutFuture.then((stream) => streamFirst(stream)),
1298 _SCHEDULE_TIMEOUT, 1331 _SCHEDULE_TIMEOUT,
1299 "waiting for the next stdout line from process $name"); 1332 "waiting for the next stdout line from process $name");
1300 }); 1333 });
1301 } 1334 }
1302 1335
1303 /// Reads the next line of stderr from the process. 1336 /// Reads the next line of stderr from the process.
1304 Future<String> nextErrLine() { 1337 Future<String> nextErrLine() {
1305 return _scheduleValue((_) { 1338 return _scheduleValue((_) {
1306 return timeout(_stderrFuture.then((stream) => readLine(stream)), 1339 return timeout(_stderrFuture.then((stream) => streamFirst(stream)),
1307 _SCHEDULE_TIMEOUT, 1340 _SCHEDULE_TIMEOUT,
1308 "waiting for the next stderr line from process $name"); 1341 "waiting for the next stderr line from process $name");
1309 }); 1342 });
1310 } 1343 }
1311 1344
1312 /// Reads the remaining stdout from the process. This should only be called 1345 /// Reads the remaining stdout from the process. This should only be called
1313 /// after kill() or shouldExit(). 1346 /// after kill() or shouldExit().
1314 Future<String> remainingStdout() { 1347 Future<String> remainingStdout() {
1315 if (!_endScheduled) { 1348 if (!_endScheduled) {
1316 throw new StateError("remainingStdout() should only be called after " 1349 throw new StateError("remainingStdout() should only be called after "
1317 "kill() or shouldExit()."); 1350 "kill() or shouldExit().");
1318 } 1351 }
1319 1352
1320 return _scheduleValue((_) { 1353 return _scheduleValue((_) {
1321 return timeout(_stdoutFuture.then(consumeStringInputStream), 1354 return timeout(_stdoutFuture.then((stream) => stream.toList())
1355 .then((lines) => lines.join("\n")),
1322 _SCHEDULE_TIMEOUT, 1356 _SCHEDULE_TIMEOUT,
1323 "waiting for the last stdout line from process $name"); 1357 "waiting for the last stdout line from process $name");
1324 }); 1358 });
1325 } 1359 }
1326 1360
1327 /// Reads the remaining stderr from the process. This should only be called 1361 /// Reads the remaining stderr from the process. This should only be called
1328 /// after kill() or shouldExit(). 1362 /// after kill() or shouldExit().
1329 Future<String> remainingStderr() { 1363 Future<String> remainingStderr() {
1330 if (!_endScheduled) { 1364 if (!_endScheduled) {
1331 throw new StateError("remainingStderr() should only be called after " 1365 throw new StateError("remainingStderr() should only be called after "
1332 "kill() or shouldExit()."); 1366 "kill() or shouldExit().");
1333 } 1367 }
1334 1368
1335 return _scheduleValue((_) { 1369 return _scheduleValue((_) {
1336 return timeout(_stderrFuture.then(consumeStringInputStream), 1370 return timeout(_stderrFuture.then((stream) => stream.toList())
1371 .then((lines) => lines.join("\n")),
1337 _SCHEDULE_TIMEOUT, 1372 _SCHEDULE_TIMEOUT,
1338 "waiting for the last stderr line from process $name"); 1373 "waiting for the last stderr line from process $name");
1339 }); 1374 });
1340 } 1375 }
1341 1376
1342 /// Writes [line] to the process as stdin. 1377 /// Writes [line] to the process as stdin.
1343 void writeLine(String line) { 1378 void writeLine(String line) {
1344 _schedule((_) => _processFuture.then( 1379 _schedule((_) => _processFuture.then(
1345 (p) => p.stdin.writeString('$line\n'))); 1380 (p) => p.stdin.write('$line\n'.charCodes)));
1346 } 1381 }
1347 1382
1348 /// Kills the process, and waits until it's dead. 1383 /// Kills the process, and waits until it's dead.
1349 void kill() { 1384 void kill() {
1350 _endScheduled = true; 1385 _endScheduled = true;
1351 _schedule((_) { 1386 _schedule((_) {
1352 _endExpected = true; 1387 _endExpected = true;
1353 _process.kill(); 1388 _process.kill();
1354 timeout(_exitCodeCompleter.future, _SCHEDULE_TIMEOUT, 1389 timeout(_exitCodeFuture, _SCHEDULE_TIMEOUT,
1355 "waiting for process $name to die"); 1390 "waiting for process $name to die");
1356 }); 1391 });
1357 } 1392 }
1358 1393
1359 /// Waits for the process to exit, and verifies that the exit code matches 1394 /// Waits for the process to exit, and verifies that the exit code matches
1360 /// [expectedExitCode] (if given). 1395 /// [expectedExitCode] (if given).
1361 void shouldExit([int expectedExitCode]) { 1396 void shouldExit([int expectedExitCode]) {
1362 _endScheduled = true; 1397 _endScheduled = true;
1363 _schedule((_) { 1398 _schedule((_) {
1364 _endExpected = true; 1399 _endExpected = true;
1365 return timeout(_exitCodeCompleter.future, _SCHEDULE_TIMEOUT, 1400 return timeout(_exitCodeFuture, _SCHEDULE_TIMEOUT,
1366 "waiting for process $name to exit").then((exitCode) { 1401 "waiting for process $name to exit").then((exitCode) {
1367 if (expectedExitCode != null) { 1402 if (expectedExitCode != null) {
1368 expect(exitCode, equals(expectedExitCode)); 1403 expect(exitCode, equals(expectedExitCode));
1369 } 1404 }
1370 }); 1405 });
1371 }); 1406 });
1372 } 1407 }
1373 1408
1374 /// Prints the remaining data in the process's stdout and stderr streams. 1409 /// Prints the remaining data in the process's stdout and stderr streams.
1375 /// Prints nothing if the straems are empty. 1410 /// Prints nothing if the streams are empty.
1376 Future _printStreams() { 1411 Future _printStreams() {
1377 Future printStream(String streamName, StringInputStream stream) { 1412 void printStream(String streamName, List<String> lines) {
1378 return consumeStringInputStream(stream).then((output) { 1413 if (lines.isEmpty) return;
1379 if (output.isEmpty) return;
1380 1414
1381 print('\nProcess $name $streamName:'); 1415 print('\nProcess $name $streamName:');
1382 for (var line in output.trim().split("\n")) { 1416 for (var line in lines) {
1383 print('| $line'); 1417 print('| $line');
1384 } 1418 }
1385 return;
1386 });
1387 } 1419 }
1388 1420
1389 return _stdoutFuture.then((stdout) { 1421 return _stdoutLines.then((stdoutLines) {
1390 return _stderrFuture.then((stderr) { 1422 printStream('stdout', stdoutLines);
1391 return printStream('stdout', stdout) 1423 return _stderrLines.then((stderrLines) {
1392 .then((_) => printStream('stderr', stderr)); 1424 printStream('stderr', stderrLines);
1393 }); 1425 });
1394 }); 1426 });
1395 } 1427 }
1396 } 1428 }
1397 1429
1398 /// A class representing an [HttpServer] that's scheduled to run in the course 1430 /// A class representing an [HttpServer] that's scheduled to run in the course
1399 /// of the test. This class allows the server's request handling to be 1431 /// of the test. This class allows the server's request handling to be
1400 /// scheduled synchronously. All operations on this class are scheduled. 1432 /// scheduled synchronously. All operations on this class are scheduled.
1401 class ScheduledServer { 1433 class ScheduledServer {
1402 /// The wrapped server. 1434 /// The wrapped server.
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
1539 /// calling [completion] is unnecessary. 1571 /// calling [completion] is unnecessary.
1540 void expectLater(Future actual, matcher, {String reason, 1572 void expectLater(Future actual, matcher, {String reason,
1541 FailureHandler failureHandler, bool verbose: false}) { 1573 FailureHandler failureHandler, bool verbose: false}) {
1542 _schedule((_) { 1574 _schedule((_) {
1543 return actual.then((value) { 1575 return actual.then((value) {
1544 expect(value, matcher, reason: reason, failureHandler: failureHandler, 1576 expect(value, matcher, reason: reason, failureHandler: failureHandler,
1545 verbose: false); 1577 verbose: false);
1546 }); 1578 });
1547 }); 1579 });
1548 } 1580 }
OLDNEW
« no previous file with comments | « utils/tests/pub/pub_uploader_test.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698