From 3093302a965f6029f795f6ee92e83a135fe4f4bc Mon Sep 17 00:00:00 2001 From: Phil Murray Date: Mon, 23 Feb 2015 16:07:46 +1300 Subject: [PATCH] Make it possible to set the encoding of strings read/written to the pipes. This changes the argument order of #popen4_ext --- lib/open4.rb | 18 +++++++++--------- test/popen4_test.rb | 24 ++++++++++++++++++++++++ test/popen4ext_test.rb | 16 ++++++++-------- 3 files changed, 41 insertions(+), 17 deletions(-) mode change 100644 => 100755 lib/open4.rb mode change 100644 => 100755 test/popen4_test.rb mode change 100644 => 100755 test/popen4ext_test.rb diff --git a/lib/open4.rb b/lib/open4.rb old mode 100644 new mode 100755 index 0cab43c..1b62278 --- a/lib/open4.rb +++ b/lib/open4.rb @@ -13,8 +13,8 @@ def Open4.description class Error < ::StandardError; end - def pfork4(fun, &b) - Open4.do_popen(b, :block) do |ps_read, _| + def pfork4(fun, encoding = nil, &b) + Open4.do_popen(b, :block, false, encoding) do |ps_read, _| ps_read.close begin fun.call @@ -30,8 +30,8 @@ def pfork4(fun, &b) end module_function :pfork4 - def popen4(*cmd, &b) - Open4.do_popen(b, :init) do |ps_read, ps_write| + def popen4(cmd, encoding = nil, &b) + Open4.do_popen(b, :init, false, encoding) do |ps_read, ps_write| ps_read.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) ps_write.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) exec(*cmd) @@ -42,8 +42,8 @@ def popen4(*cmd, &b) module_function :popen4 module_function :open4 - def popen4ext(closefds=false, *cmd, &b) - Open4.do_popen(b, :init, closefds) do |ps_read, ps_write| + def popen4ext(cmd, closefds = false, encoding = nil, &b) + Open4.do_popen(b, :init, closefds, encoding) do |ps_read, ps_write| ps_read.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) ps_write.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) exec(*cmd) @@ -52,8 +52,8 @@ def popen4ext(closefds=false, *cmd, &b) end module_function :popen4ext - def self.do_popen(b = nil, exception_propagation_at = nil, closefds=false, &cmd) - pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe + def self.do_popen(b = nil, exception_propagation_at = nil, closefds=false, encoding=nil, &cmd) + pw, pr, pe, ps = IO.pipe(encoding), IO.pipe(encoding), IO.pipe(encoding), IO.pipe(encoding) verbose = $VERBOSE begin @@ -333,7 +333,7 @@ def spawn arg, *argv begin chdir(cwd) do Timeout::timeout(timeout) do - popen4ext(closefds, *argv) do |c, i, o, e| + popen4ext(*argv, closefds) do |c, i, o, e| started = true %w( replace pid= << push update ).each do |msg| diff --git a/test/popen4_test.rb b/test/popen4_test.rb old mode 100644 new mode 100755 index 550fe53..f9808b0 --- a/test/popen4_test.rb +++ b/test/popen4_test.rb @@ -56,6 +56,30 @@ def test_io_pipes_without_block assert_equal err_msg, err_actual assert_equal 0, wait_status(cid) end + + def test_io_pipes_encoding + via_msg = "I'm so \xF0\x9F\x98\x82" + via_msg.force_encoding('ASCII-8BIT') + err_msg = "Errors make me \xF0\x9F\x98\xA0" + err_msg.force_encoding('ASCII-8BIT') + + cmd = <<-END + ruby -e " + STDOUT.write STDIN.read + STDERR.write '#{err_msg}' + " + END + cid, stdin, stdout, stderr = popen4 cmd, "ASCII-8BIT" + stdin.write via_msg + stdin.close + out_actual = stdout.read + err_actual = stderr.read + assert_equal via_msg, out_actual + assert_equal err_msg, err_actual + assert_equal 0, wait_status(cid) + assert_equal out_actual.encoding, Encoding.find("ASCII-8BIT") + end + def test_io_pipes_with_block via_msg = 'foo' diff --git a/test/popen4ext_test.rb b/test/popen4ext_test.rb old mode 100644 new mode 100755 index 5d5d0d5..47a5227 --- a/test/popen4ext_test.rb +++ b/test/popen4ext_test.rb @@ -8,23 +8,23 @@ class POpen4Test < TestCase UNKNOWN_CMD_ERRORS = [Errno::ENOENT, Errno::EINVAL] def test_unknown_command_propagates_exception - err = assert_raises(*UNKNOWN_CMD_ERRORS) { popen4ext true, UNKNOWN_CMD } + err = assert_raises(*UNKNOWN_CMD_ERRORS) { popen4ext UNKNOWN_CMD, true } assert_match(/#{UNKNOWN_CMD}/, err.to_s) if on_mri? end def test_exception_propagation_avoids_zombie_child_process - assert_raises(*UNKNOWN_CMD_ERRORS) { popen4ext true, UNKNOWN_CMD } + assert_raises(*UNKNOWN_CMD_ERRORS) { popen4ext UNKNOWN_CMD, true } assert_empty Process.waitall end def test_exit_failure code = 43 - cid, _ = popen4ext true, %{ruby -e "exit #{43}"} + cid, _ = popen4ext %{ruby -e "exit #{43}"}, true assert_equal code, wait_status(cid) end def test_exit_success - cid, _ = popen4ext true, %{ruby -e "exit"} + cid, _ = popen4ext %{ruby -e "exit"}, true assert_equal 0, wait_status(cid) end @@ -32,7 +32,7 @@ def test_passes_child_pid_to_block cmd = %{ruby -e "STDOUT.print Process.pid"} cid_in_block = nil cid_in_fun = nil - popen4ext(true, cmd) do |cid, _, stdout, _| + popen4ext(cmd, true) do |cid, _, stdout, _| cid_in_block = cid cid_in_fun = stdout.read.to_i end @@ -48,7 +48,7 @@ def test_io_pipes_without_block STDERR.write '#{err_msg}' " END - cid, stdin, stdout, stderr = popen4ext true, cmd + cid, stdin, stdout, stderr = popen4ext cmd, true stdin.write via_msg stdin.close out_actual = stdout.read @@ -68,7 +68,7 @@ def test_io_pipes_with_block STDERR.write '#{err_msg}' " END - status = popen4ext(true, cmd) do |_, stdin, stdout, stderr| + status = popen4ext(cmd, true) do |_, stdin, stdout, stderr| stdin.write via_msg stdin.close out_actual = stdout.read @@ -81,7 +81,7 @@ def test_io_pipes_with_block def test_close_ignores_errors TCPSocket.new('localhost', 59367).close rescue nil - cid, _ = popen4ext true, %{ruby -e "exit"} + cid, _ = popen4ext %{ruby -e "exit"}, true assert_equal 0, wait_status(cid) end end