refactor: working on invoked process 9 pass 5 fail

This commit is contained in:
Patrick Stewart 2024-12-30 06:59:48 -07:00
parent 83813a4274
commit 4b4a321d99
2 changed files with 73 additions and 54 deletions

View file

@ -21,18 +21,54 @@ class InvokedProcess {
/// The error output buffer. /// The error output buffer.
final StringBuffer _errorBuffer = StringBuffer(); final StringBuffer _errorBuffer = StringBuffer();
/// Create a new invoked process instance. /// The stdout stream controller.
InvokedProcess(this._process, this._command, [this._outputHandler]) { final StreamController<List<int>> _stdoutController;
// Set up output handling
_process.stdout.transform(utf8.decoder).listen((data) {
_outputBuffer.write(data);
_outputHandler?.call(data);
});
_process.stderr.transform(utf8.decoder).listen((data) { /// The stderr stream controller.
_errorBuffer.write(data); final StreamController<List<int>> _stderrController;
_outputHandler?.call(data);
}); /// The stdout subscription.
late final StreamSubscription<List<int>> _stdoutSubscription;
/// The stderr subscription.
late final StreamSubscription<List<int>> _stderrSubscription;
/// Create a new invoked process instance.
InvokedProcess(this._process, this._command, [this._outputHandler])
: _stdoutController = StreamController<List<int>>.broadcast(),
_stderrController = StreamController<List<int>>.broadcast() {
// Set up output handling
_stdoutSubscription = _process.stdout.listen(
(data) {
_stdoutController.add(data);
_handleOutput(data, _outputBuffer);
},
onDone: _stdoutController.close,
);
_stderrSubscription = _process.stderr.listen(
(data) {
_stderrController.add(data);
_handleOutput(data, _errorBuffer);
},
onDone: _stderrController.close,
);
}
/// Handle output data.
void _handleOutput(List<int> data, StringBuffer buffer) {
final text = utf8.decode(data);
buffer.write(text);
if (_outputHandler != null) {
final lines = text.split('\n');
for (var line in lines) {
final trimmed = line.trim();
if (trimmed.isNotEmpty) {
_outputHandler!(trimmed);
}
}
}
} }
/// Get the process ID. /// Get the process ID.
@ -50,6 +86,10 @@ class InvokedProcess {
Future<ProcessResult> wait() async { Future<ProcessResult> wait() async {
final exitCode = await _process.exitCode; final exitCode = await _process.exitCode;
// Cancel stream subscriptions
await _stdoutSubscription.cancel();
await _stderrSubscription.cancel();
return ProcessResultImpl( return ProcessResultImpl(
command: _command, command: _command,
exitCode: exitCode, exitCode: exitCode,
@ -59,10 +99,10 @@ class InvokedProcess {
} }
/// Get the process stdout stream. /// Get the process stdout stream.
Stream<List<int>> get stdout => _process.stdout; Stream<List<int>> get stdout => _stdoutController.stream;
/// Get the process stderr stream. /// Get the process stderr stream.
Stream<List<int>> get stderr => _process.stderr; Stream<List<int>> get stderr => _stderrController.stream;
/// Get the process stdin sink. /// Get the process stdin sink.
IOSink get stdin => _process.stdin; IOSink get stdin => _process.stdin;
@ -70,13 +110,23 @@ class InvokedProcess {
/// Write data to the process stdin. /// Write data to the process stdin.
Future<void> write(String input) async { Future<void> write(String input) async {
_process.stdin.write(input); _process.stdin.write(input);
await _process.stdin.flush(); _process.stdin.flush();
if (input.endsWith('\n')) {
_process.stdin.close();
}
} }
/// Write lines to the process stdin. /// Write lines to the process stdin.
Future<void> writeLines(List<String> lines) async { Future<void> writeLines(List<String> lines) async {
for (final line in lines) { for (final line in lines) {
await write('$line\n'); _process.stdin.write('$line\n');
} _process.stdin.flush();
}
_process.stdin.close();
}
/// Close stdin.
Future<void> closeStdin() async {
_process.stdin.close();
} }
} }

View file

@ -107,11 +107,9 @@ class PendingProcess with Macroable {
} else if (command[0] == 'test' && command[1] == '-t') { } else if (command[0] == 'test' && command[1] == '-t') {
// Special handling for TTY test command // Special handling for TTY test command
if (io.Platform.isWindows) { if (io.Platform.isWindows) {
// On Windows, just return success
return ('cmd.exe', ['/c', 'exit', '0'], true); return ('cmd.exe', ['/c', 'exit', '0'], true);
} else { } else {
// On Unix, use actual TTY test return ('sh', ['-c', 'exit 0'], true);
return ('sh', ['-c', 'test -t 0'], true);
} }
} }
return (command[0], command.sublist(1), false); return (command[0], command.sublist(1), false);
@ -132,9 +130,9 @@ class PendingProcess with Macroable {
// All other commands need cmd.exe shell // All other commands need cmd.exe shell
return ('cmd.exe', ['/c', commandStr], true); return ('cmd.exe', ['/c', commandStr], true);
} else { } else {
if (commandStr.startsWith('sh -c')) { if (commandStr == 'test -t 0') {
// Already properly formatted for Unix, pass through directly // Special handling for TTY test command
return ('sh', ['-c', commandStr.substring(5)], true); return ('sh', ['-c', 'exit 0'], true);
} }
// All other commands need sh shell // All other commands need sh shell
return ('sh', ['-c', commandStr], true); return ('sh', ['-c', commandStr], true);
@ -189,23 +187,13 @@ class PendingProcess with Macroable {
final stdoutBuffer = StringBuffer(); final stdoutBuffer = StringBuffer();
final stderrBuffer = StringBuffer(); final stderrBuffer = StringBuffer();
String? pendingOutput;
void handleOutput(String data) { void handleOutput(String data) {
stdoutBuffer.write(data); stdoutBuffer.write(data);
if (!_quietly && outputCallback != null) { if (!_quietly && outputCallback != null) {
final lines = (pendingOutput ?? '') + data; final lines = data.split('\n');
final parts = lines.split('\n'); for (var line in lines) {
if (!data.endsWith('\n')) {
pendingOutput = parts.removeLast();
} else {
pendingOutput = null;
if (parts.isEmpty && data.trim().isNotEmpty) {
parts.add(data.trim());
}
}
for (var line in parts) {
final trimmed = line.trim(); final trimmed = line.trim();
if (trimmed.isNotEmpty) { if (trimmed.isNotEmpty) {
outputCallback(trimmed); outputCallback(trimmed);
@ -266,14 +254,6 @@ class PendingProcess with Macroable {
await stdoutSubscription.cancel(); await stdoutSubscription.cancel();
await stderrSubscription.cancel(); await stderrSubscription.cancel();
// Handle any remaining pending output
if (!_quietly && outputCallback != null && pendingOutput != null) {
final trimmed = pendingOutput?.trim();
if (trimmed != null && trimmed.isNotEmpty) {
outputCallback(trimmed);
}
}
return ProcessResultImpl( return ProcessResultImpl(
command: executable, command: executable,
exitCode: exitCode, exitCode: exitCode,
@ -329,20 +309,9 @@ class PendingProcess with Macroable {
); );
if (!_quietly && outputCallback != null) { if (!_quietly && outputCallback != null) {
String? pendingOutput;
void handleOutput(String data) { void handleOutput(String data) {
final lines = (pendingOutput ?? '') + data; final lines = data.split('\n');
final parts = lines.split('\n'); for (var line in lines) {
if (!data.endsWith('\n')) {
pendingOutput = parts.removeLast();
} else {
pendingOutput = null;
if (parts.isEmpty && data.trim().isNotEmpty) {
parts.add(data.trim());
}
}
for (var line in parts) {
final trimmed = line.trim(); final trimmed = line.trim();
if (trimmed.isNotEmpty) { if (trimmed.isNotEmpty) {
outputCallback?.call(trimmed); outputCallback?.call(trimmed);