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

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

Issue 12086110: Use the dart:async Stream API thoroughly in Pub. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 10 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 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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
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
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
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 }
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