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

Side by Side Diff: pkg/scheduled_test/lib/scheduled_process.dart

Issue 132193004: Don't throw an error if a scheduled process exits early. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 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 | « no previous file | 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) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, 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 library scheduled_test.scheduled_process; 5 library scheduled_test.scheduled_process;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 import 'dart:convert'; 8 import 'dart:convert';
9 import 'dart:io'; 9 import 'dart:io';
10 10
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
58 58
59 /// A canceller that controls both [_stderr] and [_stderrLog]. 59 /// A canceller that controls both [_stderr] and [_stderrLog].
60 StreamCanceller _stderrCanceller; 60 StreamCanceller _stderrCanceller;
61 61
62 /// The exit code of the process that's scheduled to run. This will naturally 62 /// The exit code of the process that's scheduled to run. This will naturally
63 /// only complete once the process has terminated. 63 /// only complete once the process has terminated.
64 ValueFuture<int> _exitCode; 64 ValueFuture<int> _exitCode;
65 65
66 /// Whether the user has scheduled the end of this process by calling either 66 /// Whether the user has scheduled the end of this process by calling either
67 /// [shouldExit] or [kill]. 67 /// [shouldExit] or [kill].
68 var _endScheduled = false; 68 bool get _endScheduled => _scheduledExitTask != null;
69 69
70 /// The task that runs immediately before this process is scheduled to end. If 70 /// The task where this process is scheduled to exit -- either by waiting to
71 /// the process ends during this task, we treat that as expected. 71 /// exit ([shouldExit]) or by killing the process ([kill]).
72 Task _taskBeforeEnd; 72 ///
73 /// It's legal for the process to exit before this task runs. This can happen
74 /// for example if there's still standard output to read from the process
75 /// after it exits.
76 Task _scheduledExitTask;
73 77
74 /// Whether the process is expected to terminate at this point. 78 /// The task during which the process actually exited.
75 var _endExpected = false; 79 Task _actualExitTask;
76 80
77 /// Schedules a process to start. [executable], [arguments], 81 /// Schedules a process to start. [executable], [arguments],
78 /// [workingDirectory], and [environment] have the same meaning as for 82 /// [workingDirectory], and [environment] have the same meaning as for
79 /// [Process.start]. [description] is a string description of this process; it 83 /// [Process.start]. [description] is a string description of this process; it
80 /// defaults to the command-line invocation. [encoding] is the [Encoding] that 84 /// defaults to the command-line invocation. [encoding] is the [Encoding] that
81 /// will be used for the process's input and output. 85 /// will be used for the process's input and output.
82 /// 86 ///
83 /// [executable], [arguments], [workingDirectory], and [environment] may be 87 /// [executable], [arguments], [workingDirectory], and [environment] may be
84 /// either a [Future] or a concrete value. If any are [Future]s, the process 88 /// either a [Future] or a concrete value. If any are [Future]s, the process
85 /// won't start until the [Future]s have completed. In addition, [arguments] 89 /// won't start until the [Future]s have completed. In addition, [arguments]
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
166 170
167 /// Listens for [_process] to exit and passes the exit code to 171 /// Listens for [_process] to exit and passes the exit code to
168 /// [exitCodeCompleter]. If the process completes earlier than expected, an 172 /// [exitCodeCompleter]. If the process completes earlier than expected, an
169 /// exception will be signaled to the schedule. 173 /// exception will be signaled to the schedule.
170 void _handleExit(Completer exitCodeCompleter) { 174 void _handleExit(Completer exitCodeCompleter) {
171 // We purposefully avoid using wrapFuture here. If an error occurs while a 175 // We purposefully avoid using wrapFuture here. If an error occurs while a
172 // process is running, we want the schedule to move to the onException 176 // process is running, we want the schedule to move to the onException
173 // queue where the process will be killed, rather than blocking the tasks 177 // queue where the process will be killed, rather than blocking the tasks
174 // queue waiting for the process to exit. 178 // queue waiting for the process to exit.
175 _process.then((p) => Chain.track(p.exitCode)).then((exitCode) { 179 _process.then((p) => Chain.track(p.exitCode)).then((exitCode) {
176 if (_endExpected) { 180 _actualExitTask = currentSchedule.currentTask;
177 exitCodeCompleter.complete(exitCode); 181 exitCodeCompleter.complete(exitCode);
178 return;
179 }
180
181 wrapFuture(pumpEventQueue().then((_) {
182 if (currentSchedule.currentTask != _taskBeforeEnd) return null;
183 // If we're one task before the end was scheduled, wait for that task
184 // to complete and pump the event queue so that _endExpected will be
185 // set.
186 return _taskBeforeEnd.result.then((_) => pumpEventQueue());
187 }).then((_) {
188 exitCodeCompleter.complete(exitCode);
189
190 if (!_endExpected) {
191 fail("Process '$description' ended earlier than scheduled "
192 "with exit code $exitCode.");
193 }
194 }), "waiting to reach shouldExit() or kill() for process "
195 "'$description'");
196 }); 182 });
197 } 183 }
198 184
199 /// Converts a stream of bytes to a stream of lines and returns that along 185 /// Converts a stream of bytes to a stream of lines and returns that along
200 /// with a [StreamCanceller] controlling it. 186 /// with a [StreamCanceller] controlling it.
201 Pair<Stream<String>, StreamCanceller> _lineStreamWithCanceller( 187 Pair<Stream<String>, StreamCanceller> _lineStreamWithCanceller(
202 Future<Stream<List<int>>> streamFuture) { 188 Future<Stream<List<int>>> streamFuture) {
203 return streamWithCanceller(futureStream(streamFuture) 189 return streamWithCanceller(futureStream(streamFuture)
204 .handleError(currentSchedule.signalError) 190 .handleError(currentSchedule.signalError)
205 .map((chunk) { 191 .map((chunk) {
(...skipping 11 matching lines...) Expand all
217 void _scheduleExceptionCleanup() { 203 void _scheduleExceptionCleanup() {
218 currentSchedule.onException.schedule(() { 204 currentSchedule.onException.schedule(() {
219 _stdoutCanceller(); 205 _stdoutCanceller();
220 _stderrCanceller(); 206 _stderrCanceller();
221 207
222 if (!_process.hasValue) return null; 208 if (!_process.hasValue) return null;
223 209
224 var killedPrematurely = false; 210 var killedPrematurely = false;
225 if (!_exitCode.hasValue) { 211 if (!_exitCode.hasValue) {
226 killedPrematurely = true; 212 killedPrematurely = true;
227 _endExpected = true;
228 _process.value.kill(ProcessSignal.SIGKILL); 213 _process.value.kill(ProcessSignal.SIGKILL);
229 // Ensure that the onException queue waits for the process to actually 214 // Ensure that the onException queue waits for the process to actually
230 // exit after being killed. 215 // exit after being killed.
231 wrapFuture(_process.value.exitCode, "waiting for process " 216 wrapFuture(_process.value.exitCode, "waiting for process "
232 "'$description' to die"); 217 "'$description' to die");
233 } 218 }
234 219
235 return Future.wait([ 220 return Future.wait([
236 _stdoutLog.toList(), 221 _stdoutLog.toList(),
237 _stderrLog.toList() 222 _stderrLog.toList()
238 ]).then((results) { 223 ]).then((results) {
239 var stdout = results[0].join("\n"); 224 var stdout = results[0].join("\n");
240 var stderr = results[1].join("\n"); 225 var stderr = results[1].join("\n");
241 226
242 var exitDescription = killedPrematurely 227 var exitDescription;
243 ? "Process was killed prematurely." 228 if (killedPrematurely) {
244 : "Process exited with exit code ${_exitCode.value}."; 229 exitDescription = "Process was killed prematurely.";
230 } else {
231 exitDescription = "Process exited with exit code ${_exitCode.value}";
232 if (_actualExitTask != _scheduledExitTask) {
233 var taskString = _actualExitTask.toString();
234 if (taskString.contains("\n")) {
235 exitDescription += " in task:\n${prefixLines(taskString)}";
236 } else {
237 exitDescription += " in task $taskString";
238 }
239 }
240 exitDescription += ".";
241 }
242
245 currentSchedule.addDebugInfo( 243 currentSchedule.addDebugInfo(
246 "Results of running '$description':\n" 244 "Results of running '$description':\n"
247 "$exitDescription\n" 245 "$exitDescription\n"
248 "Standard output:\n" 246 "Standard output:\n"
249 "${prefixLines(stdout)}\n" 247 "${prefixLines(stdout)}\n"
250 "Standard error:\n" 248 "Standard error:\n"
251 "${prefixLines(stderr)}"); 249 "${prefixLines(stderr)}");
252 }); 250 });
253 }, "cleaning up process '$description'"); 251 }, "cleaning up process '$description'");
254 } 252 }
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
322 schedule(() => _process.then((p) => p.stdin.close()), 320 schedule(() => _process.then((p) => p.stdin.close()),
323 "closing stdin for process '$description'"); 321 "closing stdin for process '$description'");
324 } 322 }
325 323
326 /// Kills the process, and waits until it's dead. 324 /// Kills the process, and waits until it's dead.
327 void kill() { 325 void kill() {
328 if (_endScheduled) { 326 if (_endScheduled) {
329 throw new StateError("shouldExit() or kill() already called."); 327 throw new StateError("shouldExit() or kill() already called.");
330 } 328 }
331 329
332 _endScheduled = true;
333 _taskBeforeEnd = currentSchedule.tasks.contents.last;
334 schedule(() { 330 schedule(() {
335 _endExpected = true;
336 return _process 331 return _process
337 .then((p) => p.kill(ProcessSignal.SIGKILL)) 332 .then((p) => p.kill(ProcessSignal.SIGKILL))
338 .then((_) => _exitCode); 333 .then((_) => _exitCode);
339 }, "waiting for process '$description' to die"); 334 }, "waiting for process '$description' to die");
335 _scheduledExitTask = currentSchedule.tasks.contents.last;
340 } 336 }
341 337
342 /// Waits for the process to exit, and verifies that the exit code matches 338 /// Waits for the process to exit, and verifies that the exit code matches
343 /// [expectedExitCode] (if given). 339 /// [expectedExitCode] (if given).
344 void shouldExit([int expectedExitCode]) { 340 void shouldExit([int expectedExitCode]) {
345 if (_endScheduled) { 341 if (_endScheduled) {
346 throw new StateError("shouldExit() or kill() already called."); 342 throw new StateError("shouldExit() or kill() already called.");
347 } 343 }
348 344
349 _endScheduled = true;
350 _taskBeforeEnd = currentSchedule.tasks.contents.last;
351 schedule(() { 345 schedule(() {
352 _endExpected = true;
353 return _exitCode.then((exitCode) { 346 return _exitCode.then((exitCode) {
354 if (expectedExitCode != null) { 347 if (expectedExitCode != null) {
355 expect(exitCode, equals(expectedExitCode)); 348 expect(exitCode, equals(expectedExitCode));
356 } 349 }
357 }); 350 });
358 }, "waiting for process '$description' to exit"); 351 }, "waiting for process '$description' to exit");
352 _scheduledExitTask = currentSchedule.tasks.contents.last;
359 } 353 }
360 } 354 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698