1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/spec/ruby/core/process/exec_spec.rb

242 lines
8.3 KiB
Ruby
Raw Normal View History

require_relative '../../spec_helper'
describe "Process.exec" do
it "raises Errno::ENOENT for an empty string" do
2019-07-27 06:40:09 -04:00
-> { Process.exec "" }.should raise_error(Errno::ENOENT)
end
it "raises Errno::ENOENT for a command which does not exist" do
2019-07-27 06:40:09 -04:00
-> { Process.exec "bogus-noent-script.sh" }.should raise_error(Errno::ENOENT)
end
it "raises an ArgumentError if the command includes a null byte" do
2019-07-27 06:40:09 -04:00
-> { Process.exec "\000" }.should raise_error(ArgumentError)
end
unless File.executable?(__FILE__) # Some FS (e.g. vboxfs) locate all files executable
platform_is_not :windows do
it "raises Errno::EACCES when the file does not have execute permissions" do
2019-07-27 06:40:09 -04:00
-> { Process.exec __FILE__ }.should raise_error(Errno::EACCES)
end
end
platform_is :windows do
it "raises Errno::EACCES or Errno::ENOEXEC when the file is not an executable file" do
2019-07-27 06:40:09 -04:00
-> { Process.exec __FILE__ }.should raise_error(SystemCallError) { |e|
[Errno::EACCES, Errno::ENOEXEC].should include(e.class)
}
end
end
end
it "raises Errno::EACCES when passed a directory" do
-> { Process.exec File.dirname(__FILE__) }.should raise_error(Errno::EACCES)
end
it "runs the specified command, replacing current process" do
ruby_exe('Process.exec "echo hello"; puts "fail"', escape: true).should == "hello\n"
end
it "sets the current directory when given the :chdir option" do
tmpdir = tmp("")[0..-2]
platform_is_not :windows do
ruby_exe("Process.exec(\"pwd\", chdir: #{tmpdir.inspect})", escape: true).should == "#{tmpdir}\n"
end
platform_is :windows do
ruby_exe("Process.exec(\"cd\", chdir: #{tmpdir.inspect})", escape: true).tr('\\', '/').should == "#{tmpdir}\n"
end
end
it "flushes STDOUT upon exit when it's not set to sync" do
ruby_exe("STDOUT.sync = false; STDOUT.write 'hello'").should == "hello"
end
it "flushes STDERR upon exit when it's not set to sync" do
ruby_exe("STDERR.sync = false; STDERR.write 'hello'", args: "2>&1").should == "hello"
end
describe "with a single argument" do
before :each do
@dir = tmp("exec_with_dir", false)
Dir.mkdir @dir
@name = "some_file"
@path = tmp("exec_with_dir/#{@name}", false)
touch @path
end
after :each do
rm_r @path
rm_r @dir
end
platform_is_not :windows do
it "subjects the specified command to shell expansion" do
result = Dir.chdir(@dir) do
ruby_exe('Process.exec "echo *"', escape: true)
end
result.chomp.should == @name
end
it "creates an argument array with shell parsing semantics for whitespace" do
ruby_exe('Process.exec "echo a b c d"', escape: true).should == "a b c d\n"
end
end
platform_is :windows do
# There is no shell expansion on Windows
it "does not subject the specified command to shell expansion on Windows" do
result = Dir.chdir(@dir) do
ruby_exe('Process.exec "echo *"', escape: true)
end
result.should == "*\n"
end
it "does not create an argument array with shell parsing semantics for whitespace on Windows" do
ruby_exe('Process.exec "echo a b c d"', escape: true).should == "a b c d\n"
end
end
end
describe "with multiple arguments" do
it "does not subject the arguments to shell expansion" do
cmd = '"echo", "*"'
platform_is :windows do
cmd = '"cmd.exe", "/C", "echo", "*"'
end
ruby_exe("Process.exec #{cmd}", escape: true).should == "*\n"
end
end
describe "(environment variables)" do
before :each do
ENV["FOO"] = "FOO"
end
after :each do
ENV["FOO"] = nil
end
var = '$FOO'
platform_is :windows do
var = '%FOO%'
end
it "sets environment variables in the child environment" do
ruby_exe('Process.exec({"FOO" => "BAR"}, "echo ' + var + '")', escape: true).should == "BAR\n"
end
it "unsets environment variables whose value is nil" do
platform_is_not :windows do
ruby_exe('Process.exec({"FOO" => nil}, "echo ' + var + '")', escape: true).should == "\n"
end
platform_is :windows do
# On Windows, echo-ing a non-existent env var is treated as echo-ing any other string of text
ruby_exe('Process.exec({"FOO" => nil}, "echo ' + var + '")', escape: true).should == var + "\n"
end
end
it "coerces environment argument using to_hash" do
ruby_exe('o = Object.new; def o.to_hash; {"FOO" => "BAR"}; end; Process.exec(o, "echo ' + var + '")', escape: true).should == "BAR\n"
end
it "unsets other environment variables when given a true :unsetenv_others option" do
platform_is_not :windows do
ruby_exe('Process.exec("echo ' + var + '", unsetenv_others: true)', escape: true).should == "\n"
end
platform_is :windows do
ruby_exe('Process.exec("' + ENV['COMSPEC'].gsub('\\', '\\\\\\') + ' /C echo ' + var + '", unsetenv_others: true)', escape: true).should == var + "\n"
end
end
end
describe "with a command array" do
it "uses the first element as the command name and the second as the argv[0] value" do
platform_is_not :windows do
ruby_exe('Process.exec(["/bin/sh", "argv_zero"], "-c", "echo $0")', escape: true).should == "argv_zero\n"
end
platform_is :windows do
ruby_exe('Process.exec(["cmd.exe", "/C"], "/C", "echo", "argv_zero")', escape: true).should == "argv_zero\n"
end
end
it "coerces the argument using to_ary" do
platform_is_not :windows do
ruby_exe('o = Object.new; def o.to_ary; ["/bin/sh", "argv_zero"]; end; Process.exec(o, "-c", "echo $0")', escape: true).should == "argv_zero\n"
end
platform_is :windows do
ruby_exe('o = Object.new; def o.to_ary; ["cmd.exe", "/C"]; end; Process.exec(o, "/C", "echo", "argv_zero")', escape: true).should == "argv_zero\n"
end
end
it "raises an ArgumentError if the Array does not have exactly two elements" do
2019-07-27 06:40:09 -04:00
-> { Process.exec([]) }.should raise_error(ArgumentError)
-> { Process.exec([:a]) }.should raise_error(ArgumentError)
-> { Process.exec([:a, :b, :c]) }.should raise_error(ArgumentError)
end
end
platform_is_not :windows do
describe "with an options Hash" do
describe "with Integer option keys" do
before :each do
@name = tmp("exec_fd_map.txt")
@child_fd_file = tmp("child_fd_file.txt")
end
after :each do
rm_r @name, @child_fd_file
end
it "maps the key to a file descriptor in the child that inherits the file descriptor from the parent specified by the value" do
map_fd_fixture = fixture __FILE__, "map_fd.rb"
cmd = <<-EOC
2019-11-30 15:26:52 -05:00
f = File.open(#{@name.inspect}, "w+")
2020-11-13 07:17:24 -05:00
File.open(#{__FILE__.inspect}, "r") do |io|
child_fd = io.fileno
File.open(#{@child_fd_file.inspect}, "w") { |io| io.print child_fd }
Process.exec "#{ruby_cmd(map_fd_fixture)} \#{child_fd}", { child_fd => f }
end
EOC
ruby_exe(cmd, escape: true)
child_fd = IO.read(@child_fd_file).to_i
child_fd.to_i.should > STDERR.fileno
File.read(@name).should == "writing to fd: #{child_fd}"
end
2019-11-30 15:26:52 -05:00
it "lets the process after exec have specified file descriptor despite close_on_exec" do
map_fd_fixture = fixture __FILE__, "map_fd.rb"
cmd = <<-EOC
f = File.open(#{@name.inspect}, 'w+')
puts(f.fileno, f.close_on_exec?)
STDOUT.flush
Process.exec("#{ruby_cmd(map_fd_fixture)} \#{f.fileno}", f.fileno => f.fileno)
EOC
output = ruby_exe(cmd, escape: true)
child_fd, close_on_exec = output.split
child_fd.to_i.should > STDERR.fileno
close_on_exec.should == 'true'
File.read(@name).should == "writing to fd: #{child_fd}"
end
it "sets close_on_exec to false on specified fd even when it fails" do
cmd = <<-EOC
f = File.open(#{__FILE__.inspect}, 'r')
puts(f.close_on_exec?)
Process.exec('/', f.fileno => f.fileno) rescue nil
puts(f.close_on_exec?)
EOC
output = ruby_exe(cmd, escape: true)
output.split.should == ['true', 'false']
end
end
end
end
end