Make sure to drain stderr in tests so that spawned process doesn't hang.

Review URL: https://codereview.chromium.org//11474046

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@15874 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
rnystrom@google.com 2012-12-08 02:18:06 +00:00
parent dd811bb838
commit 015bb89346
3 changed files with 54 additions and 28 deletions

View file

@ -605,6 +605,16 @@ Future<String> consumeStringInputStream(StringInputStream stream) {
return completer.future;
}
/// Wrap an InputStream in a ListInputStream. This eagerly drains the [source]
/// input stream. This is useful for spawned processes which will not exit until
/// their output streams have been drained.
/// TODO(rnystrom): Get rid of this once #7218 is fixed.
InputStream wrapInputStream(InputStream source) {
var sink = new ListInputStream();
pipeInputToInput(source, sink);
return sink;
}
/// Spawns and runs the process located at [executable], passing in [args].
/// Returns a [Future] that will complete with the results of the process after
/// it has ended.

View file

@ -8,7 +8,7 @@ library log;
import 'dart:io';
import 'io.dart';
typedef LogFn(Level level, message);
typedef LogFn(Entry entry);
final Map<Level, LogFn> _loggers = new Map<Level, LogFn>();
/// The list of recorded log messages. Will only be recorded if
@ -49,9 +49,9 @@ class Level {
/// A single log entry.
class Entry {
final Level level;
final String message;
final List<String> lines;
Entry(this.level, this.message);
Entry(this.level, this.lines);
}
/// Logs [message] at [Level.ERROR].
@ -73,12 +73,13 @@ void fine(message) => write(Level.FINE, message);
void write(Level level, message) {
if (_loggers.isEmpty) showNormal();
var logFn = _loggers[level];
if (logFn != null) logFn(level, message);
var lines = message.toString().split(NEWLINE_PATTERN);
var entry = new Entry(level, lines);
if (_transcript != null) {
_transcript.add(new Entry(level, '$message'));
}
var logFn = _loggers[level];
if (logFn != null) logFn(entry);
if (_transcript != null) _transcript.add(entry);
}
/// Logs an asynchronous IO operation. Logs [startMessage] before the operation
@ -154,7 +155,7 @@ void dumpTranscript() {
stderr.writeString('---- Log transcript ----\n');
for (var entry in _transcript) {
_logToStderrWithLabel(entry.level, entry.message);
_logToStderrWithLabel(entry);
}
stderr.writeString('---- End log transcript ----\n');
}
@ -188,31 +189,40 @@ void showAll() {
}
/// Log function that prints the message to stdout.
void _logToStdout(Level level, message) {
print('$message');
void _logToStdout(Entry entry) {
_logToStream(stdout, entry, showLabel: false);
}
/// Log function that prints the message to stdout with the level name.
void _logToStdoutWithLabel(Level level, message) {
print(_splitAndPrefix(level, message));
void _logToStdoutWithLabel(Entry entry) {
_logToStream(stdout, entry, showLabel: true);
}
/// Log function that prints the message to stderr.
void _logToStderr(Level level, message) {
stderr.writeString('$message\n');
void _logToStderr(Entry entry) {
_logToStream(stderr, entry, showLabel: false);
}
/// Log function that prints the message to stderr with the level name.
void _logToStderrWithLabel(Level level, message) {
stderr.writeString(_splitAndPrefix(level, message));
stderr.writeString('\n');
void _logToStderrWithLabel(Entry entry) {
_logToStream(stderr, entry, showLabel: true);
}
/// Add the level prefix to the first line of [message] and prefix subsequent
/// lines with "|".
String _splitAndPrefix(Level level, message) {
// TODO(rnystrom): We're doing lots of splitting and joining in here. If that
// becomes a performance problem, we can optimize this to write directly to
// stdout/stderr a line at a time.
return "$level: ${Strings.join(message.toString().split('\n'), '\n | ')}";
void _logToStream(OutputStream stream, Entry entry, {bool showLabel}) {
bool firstLine = true;
for (var line in entry.lines) {
if (showLabel) {
if (firstLine) {
stream.writeString(entry.level.name);
stream.writeString(': ');
} else {
stream.writeString(' | ');
}
}
stream.writeString(line);
stream.writeString('\n');
firstLine = false;
}
}

View file

@ -1268,7 +1268,7 @@ Future<Pair<List<String>, List<String>>> schedulePackageValidation(
});
}
/// A matcher that matches a Pair.
/// A matcher that matches a Pair.
Matcher pairOf(Matcher firstMatcher, Matcher lastMatcher) =>
new _PairMatcher(firstMatcher, lastMatcher);
@ -1334,8 +1334,8 @@ class ScheduledProcess {
/// Wraps a [Process] [Future] in a scheduled process.
ScheduledProcess(this.name, Future<Process> process)
: _process = process,
_stdout = process.transform((p) => new StringInputStream(p.stdout)),
_stderr = process.transform((p) => new StringInputStream(p.stderr)) {
_stdout = process.transform((p) => _wrapStream(p.stdout)),
_stderr = process.transform((p) => _wrapStream(p.stderr)) {
_schedule((_) {
if (!_endScheduled) {
@ -1466,6 +1466,12 @@ class ScheduledProcess {
});
}
/// Wraps [source] and ensures it gets eagerly drained. We do this to make
/// sure a process will exit even if we don't care about its output.
static Future<StringInputStream> _wrapStream(InputStream source) {
return new StringInputStream(wrapInputStream(source));
}
/// Prints the remaining data in the process's stdout and stderr streams.
/// Prints nothing if the straems are empty.
Future _printStreams() {