diff --git a/Tests/CLITests/Utilities/CLITest.swift b/Tests/CLITests/Utilities/CLITest.swift index 58adf6cc..7bbe63cf 100644 --- a/Tests/CLITests/Utilities/CLITest.swift +++ b/Tests/CLITests/Utilities/CLITest.swift @@ -165,23 +165,41 @@ class CLITest { } let inputPipe = Pipe() - let outputPipe = Pipe() - let errorPipe = Pipe() process.standardInput = inputPipe - process.standardOutput = outputPipe - process.standardError = errorPipe let outputData: Data let errorData: Data do { + // Redirect stdout/stderr to temp files so the child process never + // blocks on `write()` when one stream fills the kernel pipe buffer + // before the parent drains it (issue #1456). + let tempDir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString) + try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true) + defer { + try? FileManager.default.removeItem(at: tempDir) + } + + let stdoutURL = tempDir.appendingPathComponent("stdout") + let stderrURL = tempDir.appendingPathComponent("stderr") + FileManager.default.createFile(atPath: stdoutURL.path, contents: nil) + FileManager.default.createFile(atPath: stderrURL.path, contents: nil) + + let stdoutHandle = try FileHandle(forWritingTo: stdoutURL) + defer { try? stdoutHandle.close() } + let stderrHandle = try FileHandle(forWritingTo: stderrURL) + defer { try? stderrHandle.close() } + process.standardOutput = stdoutHandle + process.standardError = stderrHandle + try process.run() if let data = stdin { inputPipe.fileHandleForWriting.write(data) } inputPipe.fileHandleForWriting.closeFile() - outputData = outputPipe.fileHandleForReading.readDataToEndOfFile() - errorData = errorPipe.fileHandleForReading.readDataToEndOfFile() process.waitUntilExit() + + outputData = try Data(contentsOf: stdoutURL) + errorData = try Data(contentsOf: stderrURL) } catch { throw CLIError.executionFailed("Failed to run CLI: \(error)") }