Skip to content

Commit

Permalink
Merge pull request #8541 from claui/fix-patch-stdout
Browse files Browse the repository at this point in the history
Return standard output in `popen_write`
  • Loading branch information
MikeMcQuaid authored Sep 2, 2020
2 parents 64eaa80 + 09d7889 commit 8604cdb
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 4 deletions.
42 changes: 40 additions & 2 deletions Library/Homebrew/test/utils/popen_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,49 @@
end

describe "::popen_write" do
it "with supports writing to a command's standard input" do
let(:foo) { mktmpdir/"foo" }

before { foo.write "Foo\n" }

it "supports writing to a command's standard input" do
subject.popen_write("grep", "-q", "success") do |pipe|
pipe.write("success\n")
pipe.write "success\n"
end
expect($CHILD_STATUS).to be_a_success
end

it "returns the command's standard output before writing" do
child_stdout = subject.popen_write("cat", foo, "-") do |pipe|
pipe.write "Bar\n"
end
expect($CHILD_STATUS).to be_a_success
expect(child_stdout).to eq <<~EOS
Foo
Bar
EOS
end

it "returns the command's standard output after writing" do
child_stdout = subject.popen_write("cat", "-", foo) do |pipe|
pipe.write "Bar\n"
end
expect($CHILD_STATUS).to be_a_success
expect(child_stdout).to eq <<~EOS
Bar
Foo
EOS
end

it "supports interleaved writing between two reads" do
child_stdout = subject.popen_write("cat", foo, "-", foo) do |pipe|
pipe.write "Bar\n"
end
expect($CHILD_STATUS).to be_a_success
expect(child_stdout).to eq <<~EOS
Foo
Bar
Foo
EOS
end
end
end
24 changes: 22 additions & 2 deletions Library/Homebrew/utils/popen.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# frozen_string_literal: true

module Utils
IO_DEFAULT_BUFFER_SIZE = 4096
private_constant :IO_DEFAULT_BUFFER_SIZE

def self.popen_read(*args, **options, &block)
popen(args, "rb", options, &block)
end
Expand All @@ -12,8 +15,25 @@ def self.safe_popen_read(*args, **options, &block)
raise ErrorDuringExecution.new(args, status: $CHILD_STATUS, output: [[:stdout, output]])
end

def self.popen_write(*args, **options, &block)
popen(args, "wb", options, &block)
def self.popen_write(*args, **options)
popen(args, "w+b", options) do |pipe|
output = ""

# Before we yield to the block, capture as much output as we can
loop do
output += pipe.read_nonblock(IO_DEFAULT_BUFFER_SIZE)
rescue IO::WaitReadable, EOFError
break
end

yield pipe
pipe.close_write
IO.select([pipe])

# Capture the rest of the output
output += pipe.read
output.freeze
end
end

def self.safe_popen_write(*args, **options, &block)
Expand Down

0 comments on commit 8604cdb

Please sign in to comment.