From 278b63a3e459993c44aa0515239970fde517642a Mon Sep 17 00:00:00 2001 From: akr Date: Thu, 24 Apr 2008 14:46:39 +0000 Subject: * include/ruby/intern.h (rb_env_clear): declared. (rb_io_mode_modenum): declared. (rb_close_before_exec): declared. (struct rb_exec_arg): add options and redirect_fds field. (rb_check_argv): removed. (rb_exec_initarg): declared. (rb_exec_getargs): declared. (rb_exec_initarg2): declared. (rb_fork): add third argument: fds. * io.c (max_file_descriptor): new static variable to record maximum file descriptor ruby used. (UPDATE_MAXFD): new macro. (UPDATE_MAXFD_PIPE): new macro. (rb_io_mode_modenum): externed. (rb_sysopen): update max_file_descriptor. (rb_close_before_exec): new function. (popen_exec): redirection removed because it is done by extended spawn mechanism. (pipe_open): generate a hash for spawn options to specify redirections. (pipe_open_v): use rb_exec_getargs. (pipe_open_s): use rb_exec_getargs. (rb_io_initialize): update max_file_descriptor.. * process.c (hide_obj): new function. (check_exec_redirect_fd): new function. (check_exec_redirect): new function. (check_exec_options_i): new function. (check_exec_fds): new function. (rb_check_exec_options): new function. (check_exec_env_i): new function. (rb_check_exec_env): new function. (rb_exec_getargs): new function. (rb_exec_initarg2): new function. (rb_exec_initarg): new function. (rb_f_exec): use rb_exec_initarg. (intcmp): new function. (run_exec_dup2): new function. (run_exec_close): new function. (run_exec_open): new function. (run_exec_pgroup): new function. (run_exec_rlimit): new function. (run_exec_options): new function. (rb_exec): call run_exec_options. (move_fds_to_avoid_crash): new function. (pipe_nocrash): new function. (rb_fork): use pipe_nocrash to avoid file descriptor conflicts. (rb_spawn): use rb_exec_initarg. (rlimit_resource_name2int): extracted from rlimit_resource_type. (rlimit_type_by_hname): new function. (rlimit_type_by_lname): new function. (rlimit_resource_type): use rlimit_type_by_hname. (proc_daemon): add fds argument for rb_fork. * hash.c (rb_env_clear): renamed from env_clear and externed. [ruby-dev:34086] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@16183 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- test/ruby/test_process.rb | 430 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 430 insertions(+) (limited to 'test/ruby/test_process.rb') diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb index 7ed0029565..5213730a2b 100644 --- a/test/ruby/test_process.rb +++ b/test/ruby/test_process.rb @@ -1,4 +1,7 @@ require 'test/unit' +require 'tmpdir' +require 'require_relative' +require_relative 'envutil' class TestProcess < Test::Unit::TestCase def test_rlimit_availability @@ -74,4 +77,431 @@ class TestProcess < Test::Unit::TestCase assert_raise(Errno::EPERM) { Process.setrlimit(:NOFILE, :INFINITY) } assert_raise(Errno::EPERM) { Process.setrlimit(:NOFILE, "INFINITY") } end + + def with_tmpchdir + Dir.mktmpdir {|d| + Dir.chdir(d) { + yield d + } + } + end + + def test_execopts_opts + assert_nothing_raised { + Process.wait Process.spawn("true", {}) + } + assert_raise(ArgumentError) { + Process.wait Process.spawn("true", :foo => 100) + } + assert_raise(ArgumentError) { + Process.wait Process.spawn("true", Process => 100) + } + end + + def test_execopts_pgroup + ruby = EnvUtil.rubybin + assert_nothing_raised { system("true", :pgroup=>false) } + + io = IO.popen([ruby, "-e", "print Process.getpgrp"]) + assert_equal(Process.getpgrp.to_s, io.read) + io.close + + io = IO.popen([ruby, "-e", "print Process.getpgrp", :pgroup=>true]) + assert_equal(io.pid.to_s, io.read) + io.close + + assert_raise(ArgumentError) { system("true", :pgroup=>-1) } + assert_raise(Errno::EPERM) { Process.wait spawn("true", :pgroup=>1) } + + io1 = IO.popen([ruby, "-e", "print Process.getpgrp", :pgroup=>true]) + io2 = IO.popen([ruby, "-e", "print Process.getpgrp", :pgroup=>io1.pid]) + assert_equal(io1.pid.to_s, io1.read) + assert_equal(io1.pid.to_s, io2.read) + Process.wait io1.pid + Process.wait io2.pid + io1.close + io2.close + + end + + def test_execopts_rlimit + return unless rlimit_exist? + assert_raise(ArgumentError) { system("true", :rlimit_foo=>0) } + assert_raise(ArgumentError) { system("true", :rlimit_NOFILE=>0) } + assert_raise(ArgumentError) { system("true", :rlimit_nofile=>[]) } + assert_raise(ArgumentError) { system("true", :rlimit_nofile=>[1,2,3]) } + + max = Process.getrlimit(:CORE).last + + n = max + IO.popen([EnvUtil.rubybin, "-e", + "p Process.getrlimit(:CORE)", :rlimit_core=>n]) {|io| + assert_equal("[#{n}, #{n}]\n", io.read) + } + + n = 0 + IO.popen([EnvUtil.rubybin, "-e", + "p Process.getrlimit(:CORE)", :rlimit_core=>n]) {|io| + assert_equal("[#{n}, #{n}]\n", io.read) + } + + n = max + IO.popen([EnvUtil.rubybin, "-e", + "p Process.getrlimit(:CORE)", :rlimit_core=>[n]]) {|io| + assert_equal("[#{n}, #{n}]", io.read.chomp) + } + + m, n = 0, max + IO.popen([EnvUtil.rubybin, "-e", + "p Process.getrlimit(:CORE)", :rlimit_core=>[m,n]]) {|io| + assert_equal("[#{m}, #{n}]", io.read.chomp) + } + + m, n = 0, 0 + IO.popen([EnvUtil.rubybin, "-e", + "p Process.getrlimit(:CORE)", :rlimit_core=>[m,n]]) {|io| + assert_equal("[#{m}, #{n}]", io.read.chomp) + } + + n = max + IO.popen([EnvUtil.rubybin, "-e", + "p Process.getrlimit(:CORE), Process.getrlimit(:CPU)", + :rlimit_core=>n, :rlimit_cpu=>3600]) {|io| + assert_equal("[#{n}, #{n}]\n[3600, 3600]", io.read.chomp) + } + end + + def test_execopts_env + assert_raise(ArgumentError) { + system({"F=O"=>"BAR"}, "env") + } + + h = {} + ENV.each {|k,v| h[k] = nil unless k == "PATH" } + IO.popen([h, "env"]) {|io| + assert_equal(1, io.readlines.length) + } + + IO.popen([{"FOO"=>"BAR"}, "env"]) {|io| + assert_match(/FOO=BAR/, io.read) + } + + with_tmpchdir {|d| + system({"fofo"=>"haha"}, "env", STDOUT=>"out") + assert_match(/fofo=haha/, File.read("out").chomp) + } + end + + def test_execopts_unsetenv_others + IO.popen(["/usr/bin/env", :unsetenv_others=>true]) {|io| + assert_equal("", io.read) + } + IO.popen([{"A"=>"B"}, "/usr/bin/env", :unsetenv_others=>true]) {|io| + assert_equal("A=B\n", io.read) + } + end + + def test_execopts_chdir + with_tmpchdir {|d| + Process.wait Process.spawn("pwd > dir", :chdir => d) + assert_equal(d, File.read("#{d}/dir").chomp) + assert_raise(Errno::ENOENT) { + Process.wait Process.spawn("true", :chdir => "d/notexist") + } + } + end + + def test_execopts_umask + with_tmpchdir {|d| + n = "#{d}/mask" + Process.wait Process.spawn("sh -c umask > #{n}", :umask => 0) + assert_equal("0000", File.read(n).chomp) + Process.wait Process.spawn("sh -c umask > #{n}", :umask => 0777) + assert_equal("0777", File.read(n).chomp) + } + end + + def with_pipe + begin + r, w = IO.pipe + yield r, w + ensure + r.close unless r.closed? + w.close unless w.closed? + end + end + + def with_pipes(n) + ary = [] + begin + n.times { + ary << IO.pipe + } + yield ary + ensure + ary.each {|r, w| + r.close unless r.closed? + w.close unless w.closed? + } + end + end + + def test_execopts_redirect + with_tmpchdir {|d| + Process.wait Process.spawn("echo a", STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]) + assert_equal("a", File.read("out").chomp) + Process.wait Process.spawn("echo 0", STDOUT=>["out", File::WRONLY|File::CREAT|File::APPEND, 0644]) + assert_equal("a\n0\n", File.read("out")) + Process.wait Process.spawn("sort", STDIN=>["out", File::RDONLY, 0644], + STDOUT=>["out2", File::WRONLY|File::CREAT|File::TRUNC, 0644]) + assert_equal("0\na\n", File.read("out2")) + Process.wait Process.spawn("echo b", [STDOUT, STDERR]=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]) + assert_equal("b", File.read("out").chomp) + Process.wait Process.spawn("echo a", STDOUT=>:close, STDERR=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]) + #p File.read("out") + assert(!File.read("out").empty?) # error message such as "echo: write error: Bad file descriptor\n". + Process.wait Process.spawn("echo c", STDERR=>STDOUT, STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]) + assert_equal("c", File.read("out").chomp) + File.open("out", "w") {|f| + Process.wait Process.spawn("echo d", f=>STDOUT, STDOUT=>f) + assert_equal("d", File.read("out").chomp) + } + Process.wait Process.spawn("echo e", STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644], + 3=>STDOUT, 4=>STDOUT, 5=>STDOUT, 6=>STDOUT, 7=>STDOUT) + assert_equal("e", File.read("out").chomp) + File.open("out", "w") {|f| + h = {STDOUT=>f, f=>STDOUT} + 3.upto(30) {|i| h[i] = STDOUT if f.fileno != i } + Process.wait Process.spawn("echo f", h) + assert_equal("f", File.read("out").chomp) + } + assert_raise(ArgumentError) { + Process.wait Process.spawn("echo f", 1=>Process) + } + assert_raise(ArgumentError) { + Process.wait Process.spawn("echo f", [Process]=>1) + } + assert_raise(ArgumentError) { + Process.wait Process.spawn("echo f", [1, STDOUT]=>2) + } + assert_raise(ArgumentError) { + Process.wait Process.spawn("echo f", -1=>2) + } + Process.wait Process.spawn("echo hhh; echo ggg", STDOUT=>"out") + assert_equal("hhh\nggg\n", File.read("out")) + Process.wait Process.spawn("sort", STDIN=>"out", STDOUT=>"out2") + assert_equal("ggg\nhhh\n", File.read("out2")) + + assert_raise(Errno::ENOENT) { + Process.wait Process.spawn("non-existing-command", (3..100).to_a=>["err", File::WRONLY|File::CREAT]) + } + assert_equal("", File.read("err")) + + system("echo bb; echo aa", STDOUT=>["out", "w"]) + assert_equal("bb\naa\n", File.read("out")) + system("sort", STDIN=>["out"], STDOUT=>"out2") + assert_equal("aa\nbb\n", File.read("out2")) + + with_pipe {|r1, w1| + with_pipe {|r2, w2| + pid = spawn("sort", STDIN=>r1, STDOUT=>w2, w1=>:close, r2=>:close) + r1.close + w2.close + w1.puts "c" + w1.puts "a" + w1.puts "b" + w1.close + assert_equal("a\nb\nc\n", r2.read) + } + } + + with_pipes(5) {|pipes| + ios = pipes.flatten + h = {} + ios.length.times {|i| h[ios[i]] = ios[(i-1)%ios.length] } + h2 = h.invert + rios = pipes.map {|r, w| r } + wios = pipes.map {|r, w| w } + child_wfds = wios.map {|w| h2[w].fileno } + pid = spawn(EnvUtil.rubybin, "-e", + "[#{child_wfds.join(',')}].each {|fd| IO.new(fd).puts fd }", h) + pipes.each {|r, w| + assert_equal("#{h2[w].fileno}\n", r.gets) + } + Process.wait pid; + } + + with_pipes(5) {|pipes| + ios = pipes.flatten + h = {} + ios.length.times {|i| h[ios[i]] = ios[(i+1)%ios.length] } + h2 = h.invert + rios = pipes.map {|r, w| r } + wios = pipes.map {|r, w| w } + child_wfds = wios.map {|w| h2[w].fileno } + pid = spawn(EnvUtil.rubybin, "-e", + "[#{child_wfds.join(',')}].each {|fd| IO.new(fd).puts fd }", h) + pipes.each {|r, w| + assert_equal("#{h2[w].fileno}\n", r.gets) + } + Process.wait pid; + } + + closed_fd = nil + with_pipes(5) {|pipes| + io = pipes.last.last + closed_fd = io.fileno + } + assert_raise(Errno::EBADF) { Process.wait spawn("true", closed_fd=>closed_fd) } + + with_pipe {|r, w| + w.close_on_exec = true + pid = spawn(EnvUtil.rubybin, "-e", "IO.new(#{w.fileno}).print 'a'", w=>w) + w.close + assert_equal("a", r.read) + Process.wait pid + } + + system("echo funya", :out=>"out") + assert_equal("funya\n", File.read("out")) + system("echo henya 1>&2", :err=>"out") + assert_equal("henya\n", File.read("out")) + IO.popen(["cat", :in=>"out"]) {|io| + assert_equal("henya\n", io.read) + } + } + end + + def test_execopts_exec + with_tmpchdir {|d| + pid = fork { + exec "echo aaa", STDOUT=>"foo" + } + Process.wait pid + assert_equal("aaa\n", File.read("foo")) + } + end + + def test_execopts_popen + with_tmpchdir {|d| + IO.popen("echo foo") {|io| assert_equal("foo\n", io.read) } + assert_raise(Errno::ENOENT) { IO.popen(["echo bar"]) {} } + IO.popen(["echo", "baz"]) {|io| assert_equal("baz\n", io.read) } + #IO.popen(["echo", "qux", STDOUT=>STDOUT]) {|io| assert_equal("qux\n", io.read) } + IO.popen(["echo", "hoge", STDERR=>STDOUT]) {|io| + assert_equal("hoge\n", io.read) + } + #IO.popen(["echo", "fuga", STDOUT=>"out"]) {|io| + # assert_equal("", io.read) + #} + #assert_equal("fuga\n", File.read("out")) + #IO.popen(["sh", "-c", "echo a >&3", 3=>STDOUT]) {|io| + # assert_equal("a\n", io.read) + #} + IO.popen(["sh", "-c", "echo b >&9", + 9=>["out2", File::WRONLY|File::CREAT|File::TRUNC]]) {|io| + assert_equal("", io.read) + } + assert_equal("b\n", File.read("out2")) + IO.popen("-") {|io| + if !io + puts "fooo" + else + assert_equal("fooo\n", io.read) + end + } + } + end + + def test_fd_inheritance + with_pipe {|r, w| + system("echo ba >&#{w.fileno}") + w.close + assert_equal("ba\n", r.read) + } + with_pipe {|r, w| + Process.wait spawn("echo bi >&#{w.fileno}") + w.close + assert_equal("bi\n", r.read) + } + with_pipe {|r, w| + Process.wait fork { exec("echo bu >&#{w.fileno}") } + w.close + assert_equal("bu\n", r.read) + } + with_pipe {|r, w| + io = IO.popen("echo be 2>&1 >&#{w.fileno}") + w.close + errmsg = io.read + assert_equal("", r.read) + assert_not_equal("", errmsg) + } + with_pipe {|r, w| + io = IO.popen([EnvUtil.rubybin, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}).puts('me')"]) + w.close + errmsg = io.read + assert_equal("", r.read) + assert_not_equal("", errmsg) + } + with_pipe {|r, w| + errmsg = `echo bo 2>&1 >&#{w.fileno}` + w.close + assert_equal("", r.read) + assert_not_equal("", errmsg) + } + end + + def test_execopts_close_others + with_tmpchdir {|d| + with_pipe {|r, w| + system("exec 2>err; echo ma >&#{w.fileno}", :close_others=>true) + w.close + assert_equal("", r.read) + assert_not_equal("", File.read("err")) + File.unlink("err") + } + with_pipe {|r, w| + Process.wait spawn("exec 2>err; echo mi >&#{w.fileno}", :close_others=>true) + w.close + assert_equal("", r.read) + assert_not_equal("", File.read("err")) + File.unlink("err") + } + with_pipe {|r, w| + Process.wait fork { exec("exec 2>err; echo mu >&#{w.fileno}", :close_others=>true) } + w.close + assert_equal("", r.read) + assert_not_equal("", File.read("err")) + File.unlink("err") + } + with_pipe {|r, w| + io = IO.popen([EnvUtil.rubybin, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}).puts('me')", :close_others=>true]) + w.close + errmsg = io.read + assert_equal("", r.read) + assert_not_equal("", errmsg) + } + with_pipe {|r, w| + io = IO.popen([EnvUtil.rubybin, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}).puts('mo')", :close_others=>false]) + w.close + errmsg = io.read + assert_equal("mo\n", r.read) + assert_equal("", errmsg) + } + with_pipe {|r, w| + io = IO.popen([EnvUtil.rubybin, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}).puts('mo')", :close_others=>nil]) + w.close + errmsg = io.read + assert_equal("mo\n", r.read) + assert_equal("", errmsg) + } + + } + end + + def test_system + str = "echo fofo" + assert_nil(system([str, str])) + end + end -- cgit v1.2.3