diff options
Diffstat (limited to 'spec/ruby/core/io/popen_spec.rb')
| -rw-r--r-- | spec/ruby/core/io/popen_spec.rb | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/spec/ruby/core/io/popen_spec.rb b/spec/ruby/core/io/popen_spec.rb new file mode 100644 index 0000000000..b5747bf255 --- /dev/null +++ b/spec/ruby/core/io/popen_spec.rb @@ -0,0 +1,287 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative '../process/fixtures/common' + +describe "IO.popen" do + ProcessSpecs.use_system_ruby(self) + + before :each do + @fname = tmp("IO_popen_spec") + @io = nil + @var = "$FOO" + platform_is :windows do + @var = "%FOO%" + end + end + + after :each do + @io.close if @io and !@io.closed? + rm_r @fname + end + + it "returns an open IO" do + @io = IO.popen(ruby_cmd('exit'), "r") + @io.closed?.should == false + end + + it "reads a read-only pipe" do + @io = IO.popen('echo foo', "r") + @io.read.should == "foo\n" + end + + it "raises IOError when writing a read-only pipe" do + @io = IO.popen('echo foo', "r") + -> { @io.write('bar') }.should.raise(IOError) + @io.read.should == "foo\n" + end + + it "sees an infinitely looping subprocess exit when read pipe is closed" do + io = IO.popen ruby_cmd('r = loop{puts "y"; 0} rescue 1; exit r'), 'r' + io.close + + $?.exitstatus.should_not == 0 + end + + it "writes to a write-only pipe" do + @io = IO.popen(ruby_cmd('IO.copy_stream(STDIN,STDOUT)', args: "> #{@fname}"), "w") + @io.write("bar") + @io.close + + File.read(@fname).should == "bar" + end + + it "raises IOError when reading a write-only pipe" do + @io = IO.popen(ruby_cmd('IO.copy_stream(STDIN,STDOUT)'), "w") + -> { @io.read }.should.raise(IOError) + end + + it "reads and writes a read/write pipe" do + @io = IO.popen(ruby_cmd('IO.copy_stream(STDIN,STDOUT)'), "r+") + @io.write("bar") + @io.read(3).should == "bar" + end + + it "waits for the child to finish" do + @io = IO.popen(ruby_cmd('IO.copy_stream(STDIN,STDOUT)', args: "> #{@fname}"), "w") + @io.write("bar") + @io.close + + $?.exitstatus.should == 0 + + File.read(@fname).should == "bar" + end + + it "does not throw an exception if child exited and has been waited for" do + @io = IO.popen([*ruby_exe, '-e', 'sleep']) + pid = @io.pid + Process.kill "KILL", pid + @io.close + platform_is_not :windows do + $?.should.signaled? + end + platform_is :windows do + $?.should.exited? + end + end + + it "returns an instance of a subclass when called on a subclass" do + @io = IOSpecs::SubIO.popen(ruby_cmd('exit'), "r") + @io.should.instance_of?(IOSpecs::SubIO) + end + + it "coerces mode argument with #to_str" do + mode = mock("mode") + mode.should_receive(:to_str).and_return("r") + @io = IO.popen(ruby_cmd('exit 0'), mode) + end + + it "accepts a path using the chdir: keyword argument" do + path = File.dirname(@fname) + + @io = IO.popen(ruby_cmd("puts Dir.pwd"), "r", chdir: path) + @io.read.chomp.should == path + end + + it "accepts a path using the chdir: keyword argument and a coercible path" do + path = File.dirname(@fname) + object = mock("path") + object.should_receive(:to_path).and_return(path) + + @io = IO.popen(ruby_cmd("puts Dir.pwd"), "r", chdir: object) + @io.read.chomp.should == path + end + + describe "with a block" do + it "yields an open IO to the block" do + IO.popen(ruby_cmd('exit'), "r") do |io| + io.closed?.should == false + end + end + + it "yields an instance of a subclass when called on a subclass" do + IOSpecs::SubIO.popen(ruby_cmd('exit'), "r") do |io| + io.should.instance_of?(IOSpecs::SubIO) + end + end + + it "closes the IO after yielding" do + io = IO.popen(ruby_cmd('exit'), "r") { |_io| _io } + io.closed?.should == true + end + + it "allows the IO to be closed inside the block" do + io = IO.popen(ruby_cmd('exit'), 'r') { |_io| _io.close; _io } + io.closed?.should == true + end + + it "returns the value of the block" do + IO.popen(ruby_cmd('exit'), "r") { :hello }.should == :hello + end + end + + platform_is_not :windows do + it "starts returns a forked process if the command is -" do + io = IO.popen("-") + + if io # parent + begin + io.gets.should == "hello from child\n" + ensure + io.close + end + else # child + puts "hello from child" + exit! + end + end + end + + it "has the given external encoding" do + @io = IO.popen(ruby_cmd('exit'), external_encoding: Encoding::EUC_JP) + @io.external_encoding.should == Encoding::EUC_JP + end + + it "has the given internal encoding" do + @io = IO.popen(ruby_cmd('exit'), internal_encoding: Encoding::EUC_JP) + @io.internal_encoding.should == Encoding::EUC_JP + end + + it "sets the internal encoding to nil if it's the same as the external encoding" do + @io = IO.popen(ruby_cmd('exit'), external_encoding: Encoding::EUC_JP, + internal_encoding: Encoding::EUC_JP) + @io.internal_encoding.should == nil + end + + context "with a leading ENV Hash" do + it "accepts a single String command" do + IO.popen({"FOO" => "bar"}, "echo #{@var}") do |io| + io.read.should == "bar\n" + end + end + + it "accepts a single String command, and an IO mode" do + IO.popen({"FOO" => "bar"}, "echo #{@var}", "r") do |io| + io.read.should == "bar\n" + end + end + + it "accepts a single String command with a trailing Hash of Process.exec options" do + IO.popen({"FOO" => "bar"}, ruby_cmd('STDERR.puts ENV["FOO"]'), + err: [:child, :out]) do |io| + io.read.should == "bar\n" + end + end + + it "accepts a single String command with a trailing Hash of Process.exec options, and an IO mode" do + IO.popen({"FOO" => "bar"}, ruby_cmd('STDERR.puts ENV["FOO"]'), "r", + err: [:child, :out]) do |io| + io.read.should == "bar\n" + end + end + + it "accepts an Array of command and arguments" do + exe, *args = ruby_exe + IO.popen({"FOO" => "bar"}, [[exe, "specfu"], *args, "-e", "puts ENV['FOO']"]) do |io| + io.read.should == "bar\n" + end + end + + it "accepts an Array of command and arguments, and an IO mode" do + exe, *args = ruby_exe + IO.popen({"FOO" => "bar"}, [[exe, "specfu"], *args, "-e", "puts ENV['FOO']"], "r") do |io| + io.read.should == "bar\n" + end + end + + it "accepts an Array command with a separate trailing Hash of Process.exec options" do + IO.popen({"FOO" => "bar"}, [*ruby_exe, "-e", "STDERR.puts ENV['FOO']"], + err: [:child, :out]) do |io| + io.read.should == "bar\n" + end + end + + it "accepts an Array command with a separate trailing Hash of Process.exec options, and an IO mode" do + IO.popen({"FOO" => "bar"}, [*ruby_exe, "-e", "STDERR.puts ENV['FOO']"], + "r", err: [:child, :out]) do |io| + io.read.should == "bar\n" + end + end + end + + context "with a leading Array argument" do + it "uses the Array as command plus args for the child process" do + IO.popen([*ruby_exe, "-e", "puts 'hello'"]) do |io| + io.read.should == "hello\n" + end + end + + it "accepts a leading ENV Hash" do + IO.popen([{"FOO" => "bar"}, *ruby_exe, "-e", "puts ENV['FOO']"]) do |io| + io.read.should == "bar\n" + end + end + + it "accepts a trailing Hash of Process.exec options" do + IO.popen([*ruby_exe, "does_not_exist", {err: [:child, :out]}]) do |io| + io.read.should =~ /LoadError/ + end + end + + it "accepts an IO mode argument following the Array" do + IO.popen([*ruby_exe, "does_not_exist", {err: [:child, :out]}], "r") do |io| + io.read.should =~ /LoadError/ + end + end + + it "accepts [env, command, arg1, arg2, ..., exec options]" do + IO.popen([{"FOO" => "bar"}, *ruby_exe, "-e", "STDERR.puts ENV[:FOO.to_s]", + err: [:child, :out]]) do |io| + io.read.should == "bar\n" + end + end + + it "accepts '[env, command, arg1, arg2, ..., exec options], mode'" do + IO.popen([{"FOO" => "bar"}, *ruby_exe, "-e", "STDERR.puts ENV[:FOO.to_s]", + err: [:child, :out]], "r") do |io| + io.read.should == "bar\n" + end + end + + it "accepts '[env, command, arg1, arg2, ..., exec options], mode, IO options'" do + IO.popen([{"FOO" => "bar"}, *ruby_exe, "-e", "STDERR.puts ENV[:FOO.to_s]", + err: [:child, :out]], "r", + internal_encoding: Encoding::EUC_JP) do |io| + io.read.should == "bar\n" + io.internal_encoding.should == Encoding::EUC_JP + end + end + + it "accepts '[env, command, arg1, arg2, ...], mode, IO + exec options'" do + IO.popen([{"FOO" => "bar"}, *ruby_exe, "-e", "STDERR.puts ENV[:FOO.to_s]"], "r", + err: [:child, :out], internal_encoding: Encoding::EUC_JP) do |io| + io.read.should == "bar\n" + io.internal_encoding.should == Encoding::EUC_JP + end + end + end +end |
