1require 'test/unit' 2require 'tempfile' 3require 'pathname' 4require 'timeout' 5require_relative 'envutil' 6require 'rbconfig' 7 8class TestProcess < Test::Unit::TestCase 9 RUBY = EnvUtil.rubybin 10 11 def setup 12 Process.waitall 13 end 14 15 def teardown 16 Process.waitall 17 end 18 19 def windows? 20 return /mswin|mingw|bccwin/ =~ RUBY_PLATFORM 21 end 22 23 def write_file(filename, content) 24 File.open(filename, "w") {|f| 25 f << content 26 } 27 end 28 29 def with_tmpchdir 30 Dir.mktmpdir {|d| 31 d = Pathname.new(d).realpath.to_s 32 Dir.chdir(d) { 33 yield d 34 } 35 } 36 end 37 38 def run_in_child(str) # should be called in a temporary directory 39 write_file("test-script", str) 40 Process.wait spawn(RUBY, "test-script") 41 $? 42 end 43 44 def test_rlimit_availability 45 begin 46 Process.getrlimit(nil) 47 rescue NotImplementedError 48 assert_raise(NotImplementedError) { Process.setrlimit } 49 rescue TypeError 50 assert_raise(ArgumentError) { Process.setrlimit } 51 end 52 end 53 54 def rlimit_exist? 55 Process.getrlimit(nil) 56 rescue NotImplementedError 57 return false 58 rescue TypeError 59 return true 60 end 61 62 def test_rlimit_nofile 63 return unless rlimit_exist? 64 with_tmpchdir { 65 write_file 's', <<-"End" 66 # if limit=0, this test freeze pn OpenBSD 67 limit = /openbsd/ =~ RUBY_PLATFORM ? 1 : 0 68 result = 1 69 begin 70 Process.setrlimit(Process::RLIMIT_NOFILE, limit) 71 rescue Errno::EINVAL 72 result = 0 73 end 74 if result == 1 75 begin 76 IO.pipe 77 rescue Errno::EMFILE 78 result = 0 79 end 80 end 81 exit result 82 End 83 pid = spawn RUBY, "s" 84 Process.wait pid 85 assert_equal(0, $?.to_i, "#{$?}") 86 } 87 end 88 89 def test_rlimit_name 90 return unless rlimit_exist? 91 [ 92 :AS, "AS", 93 :CORE, "CORE", 94 :CPU, "CPU", 95 :DATA, "DATA", 96 :FSIZE, "FSIZE", 97 :MEMLOCK, "MEMLOCK", 98 :MSGQUEUE, "MSGQUEUE", 99 :NICE, "NICE", 100 :NOFILE, "NOFILE", 101 :NPROC, "NPROC", 102 :RSS, "RSS", 103 :RTPRIO, "RTPRIO", 104 :RTTIME, "RTTIME", 105 :SBSIZE, "SBSIZE", 106 :SIGPENDING, "SIGPENDING", 107 :STACK, "STACK", 108 ].each {|name| 109 if Process.const_defined? "RLIMIT_#{name}" 110 assert_nothing_raised { Process.getrlimit(name) } 111 else 112 assert_raise(ArgumentError) { Process.getrlimit(name) } 113 end 114 } 115 assert_raise(ArgumentError) { Process.getrlimit(:FOO) } 116 assert_raise(ArgumentError) { Process.getrlimit("FOO") } 117 end 118 119 def test_rlimit_value 120 return unless rlimit_exist? 121 assert_raise(ArgumentError) { Process.setrlimit(:CORE, :FOO) } 122 with_tmpchdir do 123 s = run_in_child(<<-'End') 124 cur, max = Process.getrlimit(:NOFILE) 125 Process.setrlimit(:NOFILE, [max-10, cur].min) 126 begin 127 Process.setrlimit(:NOFILE, :INFINITY) 128 rescue Errno::EPERM 129 exit false 130 end 131 End 132 assert_not_equal(0, s.exitstatus) 133 s = run_in_child(<<-'End') 134 cur, max = Process.getrlimit(:NOFILE) 135 Process.setrlimit(:NOFILE, [max-10, cur].min) 136 begin 137 Process.setrlimit(:NOFILE, "INFINITY") 138 rescue Errno::EPERM 139 exit false 140 end 141 End 142 assert_not_equal(0, s.exitstatus) 143 end 144 end 145 146 TRUECOMMAND = [RUBY, '-e', ''] 147 148 def test_execopts_opts 149 assert_nothing_raised { 150 Process.wait Process.spawn(*TRUECOMMAND, {}) 151 } 152 assert_raise(ArgumentError) { 153 Process.wait Process.spawn(*TRUECOMMAND, :foo => 100) 154 } 155 assert_raise(ArgumentError) { 156 Process.wait Process.spawn(*TRUECOMMAND, Process => 100) 157 } 158 end 159 160 def test_execopts_pgroup 161 skip "system(:pgroup) is not supported" if windows? 162 assert_nothing_raised { system(*TRUECOMMAND, :pgroup=>false) } 163 164 io = IO.popen([RUBY, "-e", "print Process.getpgrp"]) 165 assert_equal(Process.getpgrp.to_s, io.read) 166 io.close 167 168 io = IO.popen([RUBY, "-e", "print Process.getpgrp", :pgroup=>true]) 169 assert_equal(io.pid.to_s, io.read) 170 io.close 171 172 assert_raise(ArgumentError) { system(*TRUECOMMAND, :pgroup=>-1) } 173 assert_raise(Errno::EPERM) { Process.wait spawn(*TRUECOMMAND, :pgroup=>2) } 174 175 io1 = IO.popen([RUBY, "-e", "print Process.getpgrp", :pgroup=>true]) 176 io2 = IO.popen([RUBY, "-e", "print Process.getpgrp", :pgroup=>io1.pid]) 177 assert_equal(io1.pid.to_s, io1.read) 178 assert_equal(io1.pid.to_s, io2.read) 179 Process.wait io1.pid 180 Process.wait io2.pid 181 io1.close 182 io2.close 183 end 184 185 def test_execopts_rlimit 186 return unless rlimit_exist? 187 assert_raise(ArgumentError) { system(*TRUECOMMAND, :rlimit_foo=>0) } 188 assert_raise(ArgumentError) { system(*TRUECOMMAND, :rlimit_NOFILE=>0) } 189 assert_raise(ArgumentError) { system(*TRUECOMMAND, :rlimit_nofile=>[]) } 190 assert_raise(ArgumentError) { system(*TRUECOMMAND, :rlimit_nofile=>[1,2,3]) } 191 192 max = Process.getrlimit(:CORE).last 193 194 n = max 195 IO.popen([RUBY, "-e", 196 "p Process.getrlimit(:CORE)", :rlimit_core=>n]) {|io| 197 assert_equal("[#{n}, #{n}]\n", io.read) 198 } 199 200 n = 0 201 IO.popen([RUBY, "-e", 202 "p Process.getrlimit(:CORE)", :rlimit_core=>n]) {|io| 203 assert_equal("[#{n}, #{n}]\n", io.read) 204 } 205 206 n = max 207 IO.popen([RUBY, "-e", 208 "p Process.getrlimit(:CORE)", :rlimit_core=>[n]]) {|io| 209 assert_equal("[#{n}, #{n}]", io.read.chomp) 210 } 211 212 m, n = 0, max 213 IO.popen([RUBY, "-e", 214 "p Process.getrlimit(:CORE)", :rlimit_core=>[m,n]]) {|io| 215 assert_equal("[#{m}, #{n}]", io.read.chomp) 216 } 217 218 m, n = 0, 0 219 IO.popen([RUBY, "-e", 220 "p Process.getrlimit(:CORE)", :rlimit_core=>[m,n]]) {|io| 221 assert_equal("[#{m}, #{n}]", io.read.chomp) 222 } 223 224 n = max 225 IO.popen([RUBY, "-e", 226 "p Process.getrlimit(:CORE), Process.getrlimit(:CPU)", 227 :rlimit_core=>n, :rlimit_cpu=>3600]) {|io| 228 assert_equal("[#{n}, #{n}]\n[3600, 3600]", io.read.chomp) 229 } 230 end 231 232 MANDATORY_ENVS = %w[RUBYLIB] 233 case RbConfig::CONFIG['target_os'] 234 when /linux/ 235 MANDATORY_ENVS << 'LD_PRELOAD' 236 when /mswin|mingw/ 237 MANDATORY_ENVS.concat(%w[HOME USER TMPDIR]) 238 end 239 if e = RbConfig::CONFIG['LIBPATHENV'] 240 MANDATORY_ENVS << e 241 end 242 PREENVARG = ['-e', "%w[#{MANDATORY_ENVS.join(' ')}].each{|e|ENV.delete(e)}"] 243 ENVARG = ['-e', 'ENV.each {|k,v| puts "#{k}=#{v}" }'] 244 ENVCOMMAND = [RUBY].concat(PREENVARG).concat(ENVARG) 245 246 def test_execopts_env 247 assert_raise(ArgumentError) { 248 system({"F=O"=>"BAR"}, *TRUECOMMAND) 249 } 250 251 with_tmpchdir {|d| 252 prog = "#{d}/notexist" 253 e = assert_raise(Errno::ENOENT) { 254 Process.wait Process.spawn({"FOO"=>"BAR"}, prog) 255 } 256 assert_equal(prog, e.message.sub(/.* - /, '')) 257 e = assert_raise(Errno::ENOENT) { 258 Process.wait Process.spawn({"FOO"=>"BAR"}, [prog, "blar"]) 259 } 260 assert_equal(prog, e.message.sub(/.* - /, '')) 261 } 262 h = {} 263 cmd = [h, RUBY] 264 (ENV.keys + MANDATORY_ENVS).each do |k| 265 case k 266 when /\APATH\z/i 267 when *MANDATORY_ENVS 268 cmd << '-e' << "ENV.delete('#{k}')" 269 else 270 h[k] = nil 271 end 272 end 273 cmd << '-e' << 'puts ENV.keys.map{|e|e.upcase}' 274 IO.popen(cmd) {|io| 275 assert_equal("PATH\n", io.read) 276 } 277 278 IO.popen([{"FOO"=>"BAR"}, *ENVCOMMAND]) {|io| 279 assert_match(/^FOO=BAR$/, io.read) 280 } 281 282 with_tmpchdir {|d| 283 system({"fofo"=>"haha"}, *ENVCOMMAND, STDOUT=>"out") 284 assert_match(/^fofo=haha$/, File.read("out").chomp) 285 } 286 287 old = ENV["hmm"] 288 begin 289 ENV["hmm"] = "fufu" 290 IO.popen(ENVCOMMAND) {|io| assert_match(/^hmm=fufu$/, io.read) } 291 IO.popen([{"hmm"=>""}, *ENVCOMMAND]) {|io| assert_match(/^hmm=$/, io.read) } 292 IO.popen([{"hmm"=>nil}, *ENVCOMMAND]) {|io| assert_not_match(/^hmm=/, io.read) } 293 ENV["hmm"] = "" 294 IO.popen(ENVCOMMAND) {|io| assert_match(/^hmm=$/, io.read) } 295 IO.popen([{"hmm"=>""}, *ENVCOMMAND]) {|io| assert_match(/^hmm=$/, io.read) } 296 IO.popen([{"hmm"=>nil}, *ENVCOMMAND]) {|io| assert_not_match(/^hmm=/, io.read) } 297 ENV["hmm"] = nil 298 IO.popen(ENVCOMMAND) {|io| assert_not_match(/^hmm=/, io.read) } 299 IO.popen([{"hmm"=>""}, *ENVCOMMAND]) {|io| assert_match(/^hmm=$/, io.read) } 300 IO.popen([{"hmm"=>nil}, *ENVCOMMAND]) {|io| assert_not_match(/^hmm=/, io.read) } 301 ensure 302 ENV["hmm"] = old 303 end 304 end 305 306 def _test_execopts_env_popen(cmd) 307 message = cmd.inspect 308 IO.popen({"FOO"=>"BAR"}, cmd) {|io| 309 assert_equal('FOO=BAR', io.read[/^FOO=.*/], message) 310 } 311 312 old = ENV["hmm"] 313 begin 314 ENV["hmm"] = "fufu" 315 IO.popen(cmd) {|io| assert_match(/^hmm=fufu$/, io.read, message)} 316 IO.popen({"hmm"=>""}, cmd) {|io| assert_match(/^hmm=$/, io.read, message)} 317 IO.popen({"hmm"=>nil}, cmd) {|io| assert_not_match(/^hmm=/, io.read, message)} 318 ENV["hmm"] = "" 319 IO.popen(cmd) {|io| assert_match(/^hmm=$/, io.read, message)} 320 IO.popen({"hmm"=>""}, cmd) {|io| assert_match(/^hmm=$/, io.read, message)} 321 IO.popen({"hmm"=>nil}, cmd) {|io| assert_not_match(/^hmm=/, io.read, message)} 322 ENV["hmm"] = nil 323 IO.popen(cmd) {|io| assert_not_match(/^hmm=/, io.read, message)} 324 IO.popen({"hmm"=>""}, cmd) {|io| assert_match(/^hmm=$/, io.read, message)} 325 IO.popen({"hmm"=>nil}, cmd) {|io| assert_not_match(/^hmm=/, io.read, message)} 326 ensure 327 ENV["hmm"] = old 328 end 329 end 330 331 def test_execopts_env_popen_vector 332 _test_execopts_env_popen(ENVCOMMAND) 333 end 334 335 def test_execopts_env_popen_string 336 with_tmpchdir do |d| 337 open('test-script', 'w') do |f| 338 ENVCOMMAND.each_with_index do |cmd, i| 339 next if i.zero? or cmd == "-e" 340 f.puts cmd 341 end 342 end 343 _test_execopts_env_popen("#{RUBY} test-script") 344 end 345 end 346 347 def test_execopts_preserve_env_on_exec_failure 348 with_tmpchdir {|d| 349 write_file 's', <<-"End" 350 ENV["mgg"] = nil 351 prog = "./nonexistent" 352 begin 353 Process.exec({"mgg" => "mggoo"}, [prog, prog]) 354 rescue Errno::ENOENT 355 end 356 open('out', 'w') {|f| 357 f.print ENV["mgg"].inspect 358 } 359 End 360 system(RUBY, 's') 361 assert_equal(nil.inspect, File.read('out'), 362 "[ruby-core:44093] [ruby-trunk - Bug #6249]") 363 } 364 end 365 366 def test_execopts_env_single_word 367 with_tmpchdir {|d| 368 open("test_execopts_env_single_word.rb", "w") {|f| 369 f.puts "print ENV['hgga']" 370 } 371 system({"hgga"=>"ugu"}, RUBY, 372 :in => 'test_execopts_env_single_word.rb', 373 :out => 'test_execopts_env_single_word.out') 374 assert_equal('ugu', File.read('test_execopts_env_single_word.out')) 375 } 376 end 377 378 def test_execopts_unsetenv_others 379 h = {} 380 MANDATORY_ENVS.each {|k| e = ENV[k] and h[k] = e} 381 IO.popen([h, *ENVCOMMAND, :unsetenv_others=>true]) {|io| 382 assert_equal("", io.read) 383 } 384 IO.popen([h.merge("A"=>"B"), *ENVCOMMAND, :unsetenv_others=>true]) {|io| 385 assert_equal("A=B\n", io.read) 386 } 387 end 388 389 PWD = [RUBY, '-e', 'puts Dir.pwd'] 390 391 def test_execopts_chdir 392 with_tmpchdir {|d| 393 IO.popen([*PWD, :chdir => d]) {|io| 394 assert_equal(d, io.read.chomp) 395 } 396 assert_raise(Errno::ENOENT) { 397 Process.wait Process.spawn(*PWD, :chdir => "d/notexist") 398 } 399 } 400 end 401 402 def test_execopts_open_chdir 403 with_tmpchdir {|d| 404 Dir.mkdir "foo" 405 system(*PWD, :chdir => "foo", :out => "open_chdir_test") 406 assert_file.exist?("open_chdir_test") 407 assert_file.not_exist?("foo/open_chdir_test") 408 assert_equal("#{d}/foo", File.read("open_chdir_test").chomp) 409 } 410 end 411 412 UMASK = [RUBY, '-e', 'printf "%04o\n", File.umask'] 413 414 def test_execopts_umask 415 skip "umask is not supported" if windows? 416 IO.popen([*UMASK, :umask => 0]) {|io| 417 assert_equal("0000", io.read.chomp) 418 } 419 IO.popen([*UMASK, :umask => 0777]) {|io| 420 assert_equal("0777", io.read.chomp) 421 } 422 end 423 424 def with_pipe 425 begin 426 r, w = IO.pipe 427 yield r, w 428 ensure 429 r.close unless r.closed? 430 w.close unless w.closed? 431 end 432 end 433 434 def with_pipes(n) 435 ary = [] 436 begin 437 n.times { 438 ary << IO.pipe 439 } 440 yield ary 441 ensure 442 ary.each {|r, w| 443 r.close unless r.closed? 444 w.close unless w.closed? 445 } 446 end 447 end 448 449 ECHO = lambda {|arg| [RUBY, '-e', "puts #{arg.dump}; STDOUT.flush"] } 450 SORT = [RUBY, '-e', "puts ARGF.readlines.sort"] 451 CAT = [RUBY, '-e', "IO.copy_stream STDIN, STDOUT"] 452 453 def test_execopts_redirect 454 with_tmpchdir {|d| 455 Process.wait Process.spawn(*ECHO["a"], STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]) 456 assert_equal("a", File.read("out").chomp) 457 if windows? 458 # currently telling to child the file modes is not supported. 459 open("out", "a") {|f| f.write "0\n"} 460 else 461 Process.wait Process.spawn(*ECHO["0"], STDOUT=>["out", File::WRONLY|File::CREAT|File::APPEND, 0644]) 462 assert_equal("a\n0\n", File.read("out")) 463 end 464 Process.wait Process.spawn(*SORT, STDIN=>["out", File::RDONLY, 0644], 465 STDOUT=>["out2", File::WRONLY|File::CREAT|File::TRUNC, 0644]) 466 assert_equal("0\na\n", File.read("out2")) 467 Process.wait Process.spawn(*ECHO["b"], [STDOUT, STDERR]=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]) 468 assert_equal("b", File.read("out").chomp) 469 # problem occur with valgrind 470 #Process.wait Process.spawn(*ECHO["a"], STDOUT=>:close, STDERR=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]) 471 #p File.read("out") 472 #assert(!File.read("out").empty?) # error message such as "-e:1:in `flush': Bad file descriptor (Errno::EBADF)" 473 Process.wait Process.spawn(*ECHO["c"], STDERR=>STDOUT, STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]) 474 assert_equal("c", File.read("out").chomp) 475 File.open("out", "w") {|f| 476 Process.wait Process.spawn(*ECHO["d"], STDOUT=>f) 477 assert_equal("d", File.read("out").chomp) 478 } 479 opts = {STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]} 480 opts.merge(3=>STDOUT, 4=>STDOUT, 5=>STDOUT, 6=>STDOUT, 7=>STDOUT) unless windows? 481 Process.wait Process.spawn(*ECHO["e"], opts) 482 assert_equal("e", File.read("out").chomp) 483 opts = {STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]} 484 opts.merge(3=>0, 4=>:in, 5=>STDIN, 6=>1, 7=>:out, 8=>STDOUT, 9=>2, 10=>:err, 11=>STDERR) unless windows? 485 Process.wait Process.spawn(*ECHO["ee"], opts) 486 assert_equal("ee", File.read("out").chomp) 487 unless windows? 488 # passing non-stdio fds is not supported on Windows 489 File.open("out", "w") {|f| 490 h = {STDOUT=>f, f=>STDOUT} 491 3.upto(30) {|i| h[i] = STDOUT if f.fileno != i } 492 Process.wait Process.spawn(*ECHO["f"], h) 493 assert_equal("f", File.read("out").chomp) 494 } 495 end 496 assert_raise(ArgumentError) { 497 Process.wait Process.spawn(*ECHO["f"], 1=>Process) 498 } 499 assert_raise(ArgumentError) { 500 Process.wait Process.spawn(*ECHO["f"], [Process]=>1) 501 } 502 assert_raise(ArgumentError) { 503 Process.wait Process.spawn(*ECHO["f"], [1, STDOUT]=>2) 504 } 505 assert_raise(ArgumentError) { 506 Process.wait Process.spawn(*ECHO["f"], -1=>2) 507 } 508 Process.wait Process.spawn(*ECHO["hhh\nggg\n"], STDOUT=>"out") 509 assert_equal("hhh\nggg\n", File.read("out")) 510 Process.wait Process.spawn(*SORT, STDIN=>"out", STDOUT=>"out2") 511 assert_equal("ggg\nhhh\n", File.read("out2")) 512 513 unless windows? 514 # passing non-stdio fds is not supported on Windows 515 assert_raise(Errno::ENOENT) { 516 Process.wait Process.spawn("non-existing-command", (3..60).to_a=>["err", File::WRONLY|File::CREAT]) 517 } 518 assert_equal("", File.read("err")) 519 end 520 521 system(*ECHO["bb\naa\n"], STDOUT=>["out", "w"]) 522 assert_equal("bb\naa\n", File.read("out")) 523 system(*SORT, STDIN=>["out"], STDOUT=>"out2") 524 assert_equal("aa\nbb\n", File.read("out2")) 525 526 with_pipe {|r1, w1| 527 with_pipe {|r2, w2| 528 opts = {STDIN=>r1, STDOUT=>w2} 529 opts.merge(w1=>:close, r2=>:close) unless windows? 530 pid = spawn(*SORT, opts) 531 r1.close 532 w2.close 533 w1.puts "c" 534 w1.puts "a" 535 w1.puts "b" 536 w1.close 537 assert_equal("a\nb\nc\n", r2.read) 538 r2.close 539 Process.wait(pid) 540 } 541 } 542 543 unless windows? 544 # passing non-stdio fds is not supported on Windows 545 with_pipes(5) {|pipes| 546 ios = pipes.flatten 547 h = {} 548 ios.length.times {|i| h[ios[i]] = ios[(i-1)%ios.length] } 549 h2 = h.invert 550 _rios = pipes.map {|r, w| r } 551 wios = pipes.map {|r, w| w } 552 child_wfds = wios.map {|w| h2[w].fileno } 553 pid = spawn(RUBY, "-e", 554 "[#{child_wfds.join(',')}].each {|fd| IO.new(fd, 'w').puts fd }", h) 555 pipes.each {|r, w| 556 assert_equal("#{h2[w].fileno}\n", r.gets) 557 } 558 Process.wait pid; 559 } 560 561 with_pipes(5) {|pipes| 562 ios = pipes.flatten 563 h = {} 564 ios.length.times {|i| h[ios[i]] = ios[(i+1)%ios.length] } 565 h2 = h.invert 566 _rios = pipes.map {|r, w| r } 567 wios = pipes.map {|r, w| w } 568 child_wfds = wios.map {|w| h2[w].fileno } 569 pid = spawn(RUBY, "-e", 570 "[#{child_wfds.join(',')}].each {|fd| IO.new(fd, 'w').puts fd }", h) 571 pipes.each {|r, w| 572 assert_equal("#{h2[w].fileno}\n", r.gets) 573 } 574 Process.wait pid 575 } 576 577 closed_fd = nil 578 with_pipes(5) {|pipes| 579 io = pipes.last.last 580 closed_fd = io.fileno 581 } 582 assert_raise(Errno::EBADF) { Process.wait spawn(*TRUECOMMAND, closed_fd=>closed_fd) } 583 584 with_pipe {|r, w| 585 if w.respond_to?(:"close_on_exec=") 586 w.close_on_exec = true 587 pid = spawn(RUBY, "-e", "IO.new(#{w.fileno}, 'w').print 'a'", w=>w) 588 w.close 589 assert_equal("a", r.read) 590 Process.wait pid 591 end 592 } 593 end 594 595 system(*ECHO["funya"], :out=>"out") 596 assert_equal("funya\n", File.read("out")) 597 system(RUBY, '-e', 'STDOUT.reopen(STDERR); puts "henya"', :err=>"out") 598 assert_equal("henya\n", File.read("out")) 599 IO.popen([*CAT, :in=>"out"]) {|io| 600 assert_equal("henya\n", io.read) 601 } 602 } 603 end 604 605 def test_execopts_redirect_dup2_child 606 with_tmpchdir {|d| 607 Process.wait spawn(RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'", 608 STDOUT=>"out", STDERR=>[:child, STDOUT]) 609 assert_equal("errout", File.read("out")) 610 611 Process.wait spawn(RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'", 612 STDERR=>"out", STDOUT=>[:child, STDERR]) 613 assert_equal("errout", File.read("out")) 614 615 skip "inheritance of fd other than stdin,stdout and stderr is not supported" if windows? 616 Process.wait spawn(RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'", 617 STDOUT=>"out", 618 STDERR=>[:child, 3], 619 3=>[:child, 4], 620 4=>[:child, STDOUT] 621 ) 622 assert_equal("errout", File.read("out")) 623 624 IO.popen([RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'", STDERR=>[:child, STDOUT]]) {|io| 625 assert_equal("errout", io.read) 626 } 627 628 assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, STDOUT=>[:child, STDOUT]) } 629 assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, 3=>[:child, 4], 4=>[:child, 3]) } 630 assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, 3=>[:child, 4], 4=>[:child, 5], 5=>[:child, 3]) } 631 assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, STDOUT=>[:child, 3]) } 632 } 633 end 634 635 def test_execopts_exec 636 with_tmpchdir {|d| 637 write_file("s", 'exec "echo aaa", STDOUT=>"foo"') 638 pid = spawn RUBY, 's' 639 Process.wait pid 640 assert_equal("aaa\n", File.read("foo")) 641 } 642 end 643 644 def test_execopts_popen 645 with_tmpchdir {|d| 646 IO.popen("#{RUBY} -e 'puts :foo'") {|io| assert_equal("foo\n", io.read) } 647 assert_raise(Errno::ENOENT) { IO.popen(["echo bar"]) {} } # assuming "echo bar" command not exist. 648 IO.popen(ECHO["baz"]) {|io| assert_equal("baz\n", io.read) } 649 assert_raise(ArgumentError) { 650 IO.popen([*ECHO["qux"], STDOUT=>STDOUT]) {|io| } 651 } 652 IO.popen([*ECHO["hoge"], STDERR=>STDOUT]) {|io| 653 assert_equal("hoge\n", io.read) 654 } 655 assert_raise(ArgumentError) { 656 IO.popen([*ECHO["fuga"], STDOUT=>"out"]) {|io| } 657 } 658 skip "inheritance of fd other than stdin,stdout and stderr is not supported" if windows? 659 with_pipe {|r, w| 660 IO.popen([RUBY, '-e', 'IO.new(3, "w").puts("a"); puts "b"', 3=>w]) {|io| 661 assert_equal("b\n", io.read) 662 } 663 w.close 664 assert_equal("a\n", r.read) 665 } 666 IO.popen([RUBY, '-e', "IO.new(9, 'w').puts(:b)", 667 9=>["out2", File::WRONLY|File::CREAT|File::TRUNC]]) {|io| 668 assert_equal("", io.read) 669 } 670 assert_equal("b\n", File.read("out2")) 671 } 672 end 673 674 def test_popen_fork 675 IO.popen("-") {|io| 676 if !io 677 puts "fooo" 678 else 679 assert_equal("fooo\n", io.read) 680 end 681 } 682 rescue NotImplementedError 683 end 684 685 def test_fd_inheritance 686 skip "inheritance of fd other than stdin,stdout and stderr is not supported" if windows? 687 with_pipe {|r, w| 688 system(RUBY, '-e', 'IO.new(ARGV[0].to_i, "w").puts(:ba)', w.fileno.to_s, w=>w) 689 w.close 690 assert_equal("ba\n", r.read) 691 } 692 with_pipe {|r, w| 693 Process.wait spawn(RUBY, '-e', 694 'IO.new(ARGV[0].to_i, "w").puts("bi") rescue nil', 695 w.fileno.to_s) 696 w.close 697 assert_equal("", r.read) 698 } 699 with_pipe {|r, w| 700 with_tmpchdir {|d| 701 write_file("s", <<-"End") 702 exec(#{RUBY.dump}, '-e', 703 'IO.new(ARGV[0].to_i, "w").puts("bu") rescue nil', 704 #{w.fileno.to_s.dump}, :close_others=>false) 705 End 706 w.close_on_exec = false 707 Process.wait spawn(RUBY, "s", :close_others=>false) 708 w.close 709 assert_equal("bu\n", r.read) 710 } 711 } 712 with_pipe {|r, w| 713 io = IO.popen([RUBY, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}, 'w').puts('me')"]) 714 w.close 715 errmsg = io.read 716 assert_equal("", r.read) 717 assert_not_equal("", errmsg) 718 Process.wait 719 } 720 with_pipe {|r, w| 721 errmsg = `#{RUBY} -e "STDERR.reopen(STDOUT); IO.new(#{w.fileno}, 'w').puts(123)"` 722 w.close 723 assert_equal("", r.read) 724 assert_not_equal("", errmsg) 725 } 726 end 727 728 def test_execopts_close_others 729 skip "inheritance of fd other than stdin,stdout and stderr is not supported" if windows? 730 with_tmpchdir {|d| 731 with_pipe {|r, w| 732 system(RUBY, '-e', 'STDERR.reopen("err", "w"); IO.new(ARGV[0].to_i, "w").puts("ma")', w.fileno.to_s, :close_others=>true) 733 w.close 734 assert_equal("", r.read) 735 assert_not_equal("", File.read("err")) 736 File.unlink("err") 737 } 738 with_pipe {|r, w| 739 Process.wait spawn(RUBY, '-e', 'STDERR.reopen("err", "w"); IO.new(ARGV[0].to_i, "w").puts("mi")', w.fileno.to_s, :close_others=>true) 740 w.close 741 assert_equal("", r.read) 742 assert_not_equal("", File.read("err")) 743 File.unlink("err") 744 } 745 with_pipe {|r, w| 746 w.close_on_exec = false 747 Process.wait spawn(RUBY, '-e', 'IO.new(ARGV[0].to_i, "w").puts("bi")', w.fileno.to_s, :close_others=>false) 748 w.close 749 assert_equal("bi\n", r.read) 750 } 751 with_pipe {|r, w| 752 write_file("s", <<-"End") 753 exec(#{RUBY.dump}, '-e', 754 'STDERR.reopen("err", "w"); IO.new(ARGV[0].to_i, "w").puts("mu")', 755 #{w.fileno.to_s.dump}, 756 :close_others=>true) 757 End 758 Process.wait spawn(RUBY, "s", :close_others=>false) 759 w.close 760 assert_equal("", r.read) 761 assert_not_equal("", File.read("err")) 762 File.unlink("err") 763 } 764 with_pipe {|r, w| 765 io = IO.popen([RUBY, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}, 'w').puts('me')", :close_others=>true]) 766 w.close 767 errmsg = io.read 768 assert_equal("", r.read) 769 assert_not_equal("", errmsg) 770 Process.wait 771 } 772 with_pipe {|r, w| 773 w.close_on_exec = false 774 io = IO.popen([RUBY, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}, 'w').puts('mo')", :close_others=>false]) 775 w.close 776 errmsg = io.read 777 assert_equal("mo\n", r.read) 778 assert_equal("", errmsg) 779 Process.wait 780 } 781 with_pipe {|r, w| 782 w.close_on_exec = false 783 io = IO.popen([RUBY, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}, 'w').puts('mo')", :close_others=>nil]) 784 w.close 785 errmsg = io.read 786 assert_equal("mo\n", r.read) 787 assert_equal("", errmsg) 788 Process.wait 789 } 790 791 } 792 end 793 794 def test_execopts_redirect_self 795 begin 796 with_pipe {|r, w| 797 w << "haha\n" 798 w.close 799 r.close_on_exec = true 800 IO.popen([RUBY, "-e", "print IO.new(#{r.fileno}, 'r').read", r.fileno=>r.fileno, :close_others=>false]) {|io| 801 assert_equal("haha\n", io.read) 802 } 803 } 804 rescue NotImplementedError 805 skip "IO#close_on_exec= is not supported" 806 end 807 end 808 809 def test_execopts_redirect_tempfile 810 bug6269 = '[ruby-core:44181]' 811 Tempfile.open("execopts") do |tmp| 812 pid = assert_nothing_raised(ArgumentError, bug6269) do 813 break spawn(RUBY, "-e", "print $$", out: tmp) 814 end 815 Process.wait(pid) 816 tmp.rewind 817 assert_equal(pid.to_s, tmp.read) 818 tmp.close(true) 819 end 820 end 821 822 def test_execopts_duplex_io 823 IO.popen("#{RUBY} -e ''", "r+") {|duplex| 824 assert_raise(ArgumentError) { system("#{RUBY} -e ''", duplex=>STDOUT) } 825 assert_raise(ArgumentError) { system("#{RUBY} -e ''", STDOUT=>duplex) } 826 } 827 end 828 829 def test_execopts_modification 830 h = {} 831 Process.wait spawn(*TRUECOMMAND, h) 832 assert_equal({}, h) 833 834 h = {} 835 system(*TRUECOMMAND, h) 836 assert_equal({}, h) 837 838 h = {} 839 io = IO.popen([*TRUECOMMAND, h]) 840 io.close 841 assert_equal({}, h) 842 end 843 844 def test_system_noshell 845 str = "echo non existing command name which contains spaces" 846 assert_nil(system([str, str])) 847 end 848 849 def test_spawn_noshell 850 str = "echo non existing command name which contains spaces" 851 assert_raise(Errno::ENOENT) { spawn([str, str]) } 852 end 853 854 def test_popen_noshell 855 str = "echo non existing command name which contains spaces" 856 assert_raise(Errno::ENOENT) { IO.popen([str, str]) } 857 end 858 859 def test_exec_noshell 860 with_tmpchdir {|d| 861 write_file("s", <<-"End") 862 str = "echo non existing command name which contains spaces" 863 STDERR.reopen(STDOUT) 864 begin 865 exec [str, str] 866 rescue Errno::ENOENT 867 print "Errno::ENOENT success" 868 end 869 End 870 r = IO.popen([RUBY, "s", :close_others=>false], "r") {|f| f.read} 871 assert_equal("Errno::ENOENT success", r) 872 } 873 end 874 875 def test_system_wordsplit 876 with_tmpchdir {|d| 877 write_file("script", <<-'End') 878 File.open("result", "w") {|t| t << "haha pid=#{$$} ppid=#{Process.ppid}" } 879 exit 5 880 End 881 str = "#{RUBY} script" 882 ret = system(str) 883 status = $? 884 assert_equal(false, ret) 885 assert(status.exited?) 886 assert_equal(5, status.exitstatus) 887 assert_equal("haha pid=#{status.pid} ppid=#{$$}", File.read("result")) 888 } 889 end 890 891 def test_spawn_wordsplit 892 with_tmpchdir {|d| 893 write_file("script", <<-'End') 894 File.open("result", "w") {|t| t << "hihi pid=#{$$} ppid=#{Process.ppid}" } 895 exit 6 896 End 897 str = "#{RUBY} script" 898 pid = spawn(str) 899 Process.wait pid 900 status = $? 901 assert_equal(pid, status.pid) 902 assert(status.exited?) 903 assert_equal(6, status.exitstatus) 904 assert_equal("hihi pid=#{status.pid} ppid=#{$$}", File.read("result")) 905 } 906 end 907 908 def test_popen_wordsplit 909 with_tmpchdir {|d| 910 write_file("script", <<-'End') 911 print "fufu pid=#{$$} ppid=#{Process.ppid}" 912 exit 7 913 End 914 str = "#{RUBY} script" 915 io = IO.popen(str) 916 pid = io.pid 917 result = io.read 918 io.close 919 status = $? 920 assert_equal(pid, status.pid) 921 assert(status.exited?) 922 assert_equal(7, status.exitstatus) 923 assert_equal("fufu pid=#{status.pid} ppid=#{$$}", result) 924 } 925 end 926 927 def test_popen_wordsplit_beginning_and_trailing_spaces 928 with_tmpchdir {|d| 929 write_file("script", <<-'End') 930 print "fufumm pid=#{$$} ppid=#{Process.ppid}" 931 exit 7 932 End 933 str = " #{RUBY} script " 934 io = IO.popen(str) 935 pid = io.pid 936 result = io.read 937 io.close 938 status = $? 939 assert_equal(pid, status.pid) 940 assert(status.exited?) 941 assert_equal(7, status.exitstatus) 942 assert_equal("fufumm pid=#{status.pid} ppid=#{$$}", result) 943 } 944 end 945 946 def test_exec_wordsplit 947 with_tmpchdir {|d| 948 write_file("script", <<-'End') 949 File.open("result", "w") {|t| 950 if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM 951 t << "hehe ppid=#{Process.ppid}" 952 else 953 t << "hehe pid=#{$$} ppid=#{Process.ppid}" 954 end 955 } 956 exit 6 957 End 958 write_file("s", <<-"End") 959 ruby = #{RUBY.dump} 960 exec "\#{ruby} script" 961 End 962 pid = spawn(RUBY, "s") 963 Process.wait pid 964 status = $? 965 assert_equal(pid, status.pid) 966 assert(status.exited?) 967 assert_equal(6, status.exitstatus) 968 if windows? 969 expected = "hehe ppid=#{status.pid}" 970 else 971 expected = "hehe pid=#{status.pid} ppid=#{$$}" 972 end 973 assert_equal(expected, File.read("result")) 974 } 975 end 976 977 def test_system_shell 978 with_tmpchdir {|d| 979 write_file("script1", <<-'End') 980 File.open("result1", "w") {|t| t << "taka pid=#{$$} ppid=#{Process.ppid}" } 981 exit 7 982 End 983 write_file("script2", <<-'End') 984 File.open("result2", "w") {|t| t << "taki pid=#{$$} ppid=#{Process.ppid}" } 985 exit 8 986 End 987 ret = system("#{RUBY} script1 || #{RUBY} script2") 988 status = $? 989 assert_equal(false, ret) 990 assert(status.exited?) 991 result1 = File.read("result1") 992 result2 = File.read("result2") 993 assert_match(/\Ataka pid=\d+ ppid=\d+\z/, result1) 994 assert_match(/\Ataki pid=\d+ ppid=\d+\z/, result2) 995 assert_not_equal(result1[/\d+/].to_i, status.pid) 996 997 if windows? 998 Dir.mkdir(path = "path with space") 999 write_file(bat = path + "/bat test.bat", "@echo %1>out") 1000 system(bat, "foo 'bar'") 1001 assert_equal(%["foo 'bar'"\n], File.read("out"), '[ruby-core:22960]') 1002 system(%[#{bat.dump} "foo 'bar'"]) 1003 assert_equal(%["foo 'bar'"\n], File.read("out"), '[ruby-core:22960]') 1004 end 1005 } 1006 end 1007 1008 def test_spawn_shell 1009 with_tmpchdir {|d| 1010 write_file("script1", <<-'End') 1011 File.open("result1", "w") {|t| t << "taku pid=#{$$} ppid=#{Process.ppid}" } 1012 exit 7 1013 End 1014 write_file("script2", <<-'End') 1015 File.open("result2", "w") {|t| t << "take pid=#{$$} ppid=#{Process.ppid}" } 1016 exit 8 1017 End 1018 pid = spawn("#{RUBY} script1 || #{RUBY} script2") 1019 Process.wait pid 1020 status = $? 1021 assert(status.exited?) 1022 assert(!status.success?) 1023 result1 = File.read("result1") 1024 result2 = File.read("result2") 1025 assert_match(/\Ataku pid=\d+ ppid=\d+\z/, result1) 1026 assert_match(/\Atake pid=\d+ ppid=\d+\z/, result2) 1027 assert_not_equal(result1[/\d+/].to_i, status.pid) 1028 1029 if windows? 1030 Dir.mkdir(path = "path with space") 1031 write_file(bat = path + "/bat test.bat", "@echo %1>out") 1032 pid = spawn(bat, "foo 'bar'") 1033 Process.wait pid 1034 status = $? 1035 assert(status.exited?) 1036 assert(status.success?) 1037 assert_equal(%["foo 'bar'"\n], File.read("out"), '[ruby-core:22960]') 1038 pid = spawn(%[#{bat.dump} "foo 'bar'"]) 1039 Process.wait pid 1040 status = $? 1041 assert(status.exited?) 1042 assert(status.success?) 1043 assert_equal(%["foo 'bar'"\n], File.read("out"), '[ruby-core:22960]') 1044 end 1045 } 1046 end 1047 1048 def test_popen_shell 1049 with_tmpchdir {|d| 1050 write_file("script1", <<-'End') 1051 puts "tako pid=#{$$} ppid=#{Process.ppid}" 1052 exit 7 1053 End 1054 write_file("script2", <<-'End') 1055 puts "tika pid=#{$$} ppid=#{Process.ppid}" 1056 exit 8 1057 End 1058 io = IO.popen("#{RUBY} script1 || #{RUBY} script2") 1059 result = io.read 1060 io.close 1061 status = $? 1062 assert(status.exited?) 1063 assert(!status.success?) 1064 assert_match(/\Atako pid=\d+ ppid=\d+\ntika pid=\d+ ppid=\d+\n\z/, result) 1065 assert_not_equal(result[/\d+/].to_i, status.pid) 1066 1067 if windows? 1068 Dir.mkdir(path = "path with space") 1069 write_file(bat = path + "/bat test.bat", "@echo %1") 1070 r = IO.popen([bat, "foo 'bar'"]) {|f| f.read} 1071 assert_equal(%["foo 'bar'"\n], r, '[ruby-core:22960]') 1072 r = IO.popen(%[#{bat.dump} "foo 'bar'"]) {|f| f.read} 1073 assert_equal(%["foo 'bar'"\n], r, '[ruby-core:22960]') 1074 end 1075 } 1076 end 1077 1078 def test_exec_shell 1079 with_tmpchdir {|d| 1080 write_file("script1", <<-'End') 1081 File.open("result1", "w") {|t| t << "tiki pid=#{$$} ppid=#{Process.ppid}" } 1082 exit 7 1083 End 1084 write_file("script2", <<-'End') 1085 File.open("result2", "w") {|t| t << "tiku pid=#{$$} ppid=#{Process.ppid}" } 1086 exit 8 1087 End 1088 write_file("s", <<-"End") 1089 ruby = #{RUBY.dump} 1090 exec("\#{ruby} script1 || \#{ruby} script2") 1091 End 1092 pid = spawn RUBY, "s" 1093 Process.wait pid 1094 status = $? 1095 assert(status.exited?) 1096 assert(!status.success?) 1097 result1 = File.read("result1") 1098 result2 = File.read("result2") 1099 assert_match(/\Atiki pid=\d+ ppid=\d+\z/, result1) 1100 assert_match(/\Atiku pid=\d+ ppid=\d+\z/, result2) 1101 assert_not_equal(result1[/\d+/].to_i, status.pid) 1102 } 1103 end 1104 1105 def test_argv0 1106 with_tmpchdir {|d| 1107 assert_equal(false, system([RUBY, "asdfg"], "-e", "exit false")) 1108 assert_equal(true, system([RUBY, "zxcvb"], "-e", "exit true")) 1109 1110 Process.wait spawn([RUBY, "poiu"], "-e", "exit 4") 1111 assert_equal(4, $?.exitstatus) 1112 1113 assert_equal("1", IO.popen([[RUBY, "qwerty"], "-e", "print 1"]).read) 1114 Process.wait 1115 1116 write_file("s", <<-"End") 1117 exec([#{RUBY.dump}, "lkjh"], "-e", "exit 5") 1118 End 1119 pid = spawn RUBY, "s" 1120 Process.wait pid 1121 assert_equal(5, $?.exitstatus) 1122 } 1123 end 1124 1125 def with_stdin(filename) 1126 open(filename) {|f| 1127 begin 1128 old = STDIN.dup 1129 begin 1130 STDIN.reopen(filename) 1131 yield 1132 ensure 1133 STDIN.reopen(old) 1134 end 1135 ensure 1136 old.close 1137 end 1138 } 1139 end 1140 1141 def test_argv0_noarg 1142 with_tmpchdir {|d| 1143 open("t", "w") {|f| f.print "exit true" } 1144 open("f", "w") {|f| f.print "exit false" } 1145 1146 with_stdin("t") { assert_equal(true, system([RUBY, "qaz"])) } 1147 with_stdin("f") { assert_equal(false, system([RUBY, "wsx"])) } 1148 1149 with_stdin("t") { Process.wait spawn([RUBY, "edc"]) } 1150 assert($?.success?) 1151 with_stdin("f") { Process.wait spawn([RUBY, "rfv"]) } 1152 assert(!$?.success?) 1153 1154 with_stdin("t") { IO.popen([[RUBY, "tgb"]]) {|io| assert_equal("", io.read) } } 1155 assert($?.success?) 1156 with_stdin("f") { IO.popen([[RUBY, "yhn"]]) {|io| assert_equal("", io.read) } } 1157 assert(!$?.success?) 1158 1159 status = run_in_child "STDIN.reopen('t'); exec([#{RUBY.dump}, 'ujm'])" 1160 assert(status.success?) 1161 status = run_in_child "STDIN.reopen('f'); exec([#{RUBY.dump}, 'ik,'])" 1162 assert(!status.success?) 1163 } 1164 end 1165 1166 def test_status 1167 with_tmpchdir do 1168 s = run_in_child("exit 1") 1169 assert_equal("#<Process::Status: pid #{ s.pid } exit #{ s.exitstatus }>", s.inspect) 1170 1171 assert_equal(s, s) 1172 assert_equal(s, s.to_i) 1173 1174 assert_equal(s.to_i & 0x55555555, s & 0x55555555) 1175 assert_equal(s.to_i >> 1, s >> 1) 1176 assert_equal(false, s.stopped?) 1177 assert_equal(nil, s.stopsig) 1178 end 1179 end 1180 1181 def test_status_kill 1182 return unless Process.respond_to?(:kill) 1183 return unless Signal.list.include?("QUIT") 1184 1185 with_tmpchdir do 1186 write_file("foo", "sleep 30") 1187 pid = spawn(RUBY, "foo") 1188 Thread.new { sleep 1; Process.kill(:SIGQUIT, pid) } 1189 Process.wait(pid) 1190 s = $? 1191 assert_equal([false, true, false], 1192 [s.exited?, s.signaled?, s.stopped?], 1193 "[s.exited?, s.signaled?, s.stopped?]") 1194 assert_send( 1195 [["#<Process::Status: pid #{ s.pid } SIGQUIT (signal #{ s.termsig })>", 1196 "#<Process::Status: pid #{ s.pid } SIGQUIT (signal #{ s.termsig }) (core dumped)>"], 1197 :include?, 1198 s.inspect]) 1199 assert_equal(false, s.exited?) 1200 assert_equal(nil, s.success?) 1201 end 1202 end 1203 1204 def test_wait_without_arg 1205 with_tmpchdir do 1206 write_file("foo", "sleep 0.1") 1207 pid = spawn(RUBY, "foo") 1208 assert_equal(pid, Process.wait) 1209 end 1210 end 1211 1212 def test_wait2 1213 with_tmpchdir do 1214 write_file("foo", "sleep 0.1") 1215 pid = spawn(RUBY, "foo") 1216 assert_equal([pid, 0], Process.wait2) 1217 end 1218 end 1219 1220 def test_waitall 1221 with_tmpchdir do 1222 write_file("foo", "sleep 0.1") 1223 ps = (0...3).map { spawn(RUBY, "foo") }.sort 1224 ss = Process.waitall.sort 1225 ps.zip(ss) do |p1, (p2, s)| 1226 assert_equal(p1, p2) 1227 assert_equal(p1, s.pid) 1228 end 1229 end 1230 end 1231 1232 def test_abort 1233 with_tmpchdir do 1234 s = run_in_child("abort") 1235 assert_not_equal(0, s.exitstatus) 1236 end 1237 end 1238 1239 def test_sleep 1240 assert_raise(ArgumentError) { sleep(1, 1) } 1241 end 1242 1243 def test_getpgid 1244 assert_kind_of(Integer, Process.getpgid(Process.ppid)) 1245 rescue NotImplementedError 1246 end 1247 1248 def test_getpriority 1249 assert_kind_of(Integer, Process.getpriority(Process::PRIO_PROCESS, $$)) 1250 rescue NameError, NotImplementedError 1251 end 1252 1253 def test_setpriority 1254 if defined? Process::PRIO_USER 1255 assert_nothing_raised do 1256 pr = Process.getpriority(Process::PRIO_PROCESS, $$) 1257 Process.setpriority(Process::PRIO_PROCESS, $$, pr) 1258 end 1259 end 1260 end 1261 1262 def test_getuid 1263 assert_kind_of(Integer, Process.uid) 1264 end 1265 1266 def test_groups 1267 gs = Process.groups 1268 assert_instance_of(Array, gs) 1269 gs.each {|g| assert_kind_of(Integer, g) } 1270 rescue NotImplementedError 1271 end 1272 1273 def test_maxgroups 1274 assert_kind_of(Integer, Process.maxgroups) 1275 rescue NotImplementedError 1276 end 1277 1278 def test_geteuid 1279 assert_kind_of(Integer, Process.euid) 1280 end 1281 1282 def test_seteuid 1283 assert_nothing_raised(TypeError) {Process.euid += 0} 1284 rescue NotImplementedError 1285 end 1286 1287 def test_seteuid_name 1288 user = ENV["USER"] or return 1289 assert_nothing_raised(TypeError) {Process.euid = user} 1290 rescue NotImplementedError 1291 end 1292 1293 def test_getegid 1294 assert_kind_of(Integer, Process.egid) 1295 end 1296 1297 def test_setegid 1298 assert_nothing_raised(TypeError) {Process.egid += 0} 1299 rescue NotImplementedError 1300 end 1301 1302 def test_uid_re_exchangeable_p 1303 r = Process::UID.re_exchangeable? 1304 assert(true == r || false == r) 1305 end 1306 1307 def test_gid_re_exchangeable_p 1308 r = Process::GID.re_exchangeable? 1309 assert(true == r || false == r) 1310 end 1311 1312 def test_uid_sid_available? 1313 r = Process::UID.sid_available? 1314 assert(true == r || false == r) 1315 end 1316 1317 def test_gid_sid_available? 1318 r = Process::GID.sid_available? 1319 assert(true == r || false == r) 1320 end 1321 1322 def test_pst_inspect 1323 assert_nothing_raised { Process::Status.allocate.inspect } 1324 end 1325 1326 def test_wait_and_sigchild 1327 if /freebsd|openbsd/ =~ RUBY_PLATFORM 1328 # this relates #4173 1329 # When ruby can use 2 cores, signal and wait4 may miss the signal. 1330 skip "this fails on FreeBSD and OpenBSD on multithreaded environment" 1331 end 1332 signal_received = [] 1333 Signal.trap(:CHLD) { signal_received << true } 1334 pid = fork { sleep 0.1; exit } 1335 Thread.start { raise } 1336 Process.wait pid 1337 sleep 0.1 1338 assert_equal [true], signal_received, " [ruby-core:19744]" 1339 rescue NotImplementedError, ArgumentError 1340 ensure 1341 begin 1342 Signal.trap(:CHLD, 'DEFAULT') 1343 rescue ArgumentError 1344 end 1345 end 1346 1347 def test_no_curdir 1348 with_tmpchdir {|d| 1349 Dir.mkdir("vd") 1350 status = nil 1351 Dir.chdir("vd") { 1352 dir = "#{d}/vd" 1353 # OpenSolaris cannot remove the current directory. 1354 system(RUBY, "--disable-gems", "-e", "Dir.chdir '..'; Dir.rmdir #{dir.dump}") 1355 system({"RUBYLIB"=>nil}, RUBY, "--disable-gems", "-e", "exit true") 1356 status = $? 1357 } 1358 assert(status.success?, "[ruby-dev:38105]") 1359 } 1360 end unless /mswin|bccwin|mingw/ =~ RUBY_PLATFORM 1361 1362 def test_fallback_to_sh 1363 feature = '[ruby-core:32745]' 1364 with_tmpchdir do |d| 1365 open("tmp_script.#{$$}", "w") {|f| f.puts ": ;"; f.chmod(0755)} 1366 assert_not_nil(pid = Process.spawn("./tmp_script.#{$$}"), feature) 1367 wpid, st = Process.waitpid2(pid) 1368 assert_equal([pid, true], [wpid, st.success?], feature) 1369 1370 open("tmp_script.#{$$}", "w") {|f| f.puts "echo $#: $@"; f.chmod(0755)} 1371 result = IO.popen(["./tmp_script.#{$$}", "a b", "c"]) {|f| f.read} 1372 assert_equal("2: a b c\n", result, feature) 1373 1374 open("tmp_script.#{$$}", "w") {|f| f.puts "echo $hghg"; f.chmod(0755)} 1375 result = IO.popen([{"hghg" => "mogomogo"}, "./tmp_script.#{$$}", "a b", "c"]) {|f| f.read} 1376 assert_equal("mogomogo\n", result, feature) 1377 1378 end 1379 end if File.executable?("/bin/sh") 1380 1381 def test_spawn_too_long_path 1382 bug4314 = '[ruby-core:34842]' 1383 assert_fail_too_long_path(%w"echo", bug4314) 1384 end 1385 1386 def test_aspawn_too_long_path 1387 bug4315 = '[ruby-core:34833]' 1388 assert_fail_too_long_path(%w"echo |", bug4315) 1389 end 1390 1391 def assert_fail_too_long_path((cmd, sep), mesg) 1392 sep ||= "" 1393 min = 1_000 / (cmd.size + sep.size) 1394 cmds = Array.new(min, cmd) 1395 exs = [Errno::ENOENT] 1396 exs << Errno::E2BIG if defined?(Errno::E2BIG) 1397 EnvUtil.suppress_warning do 1398 assert_raise(*exs, mesg) do 1399 begin 1400 loop do 1401 Process.spawn(cmds.join(sep), [STDOUT, STDERR]=>:close) 1402 min = [cmds.size, min].max 1403 cmds *= 100 1404 end 1405 rescue NoMemoryError 1406 size = cmds.size 1407 raise if min >= size - 1 1408 min = [min, size /= 2].max 1409 cmds[size..-1] = [] 1410 raise if size < 250 1411 retry 1412 end 1413 end 1414 end 1415 end 1416 1417 def test_system_sigpipe 1418 return if windows? 1419 1420 pid = 0 1421 1422 with_tmpchdir do 1423 assert_nothing_raised('[ruby-dev:12261]') do 1424 timeout(3) do 1425 pid = spawn('yes | ls') 1426 Process.waitpid pid 1427 end 1428 end 1429 end 1430 ensure 1431 Process.kill(:KILL, pid) if (pid != 0) rescue false 1432 end 1433 1434 if Process.respond_to?(:daemon) 1435 def test_daemon_default 1436 data = IO.popen("-", "r+") do |f| 1437 break f.read if f 1438 Process.daemon 1439 puts "ng" 1440 end 1441 assert_equal("", data) 1442 end 1443 1444 def test_daemon_noclose 1445 data = IO.popen("-", "r+") do |f| 1446 break f.read if f 1447 Process.daemon(false, true) 1448 puts "ok", Dir.pwd 1449 end 1450 assert_equal("ok\n/\n", data) 1451 end 1452 1453 def test_daemon_nochdir_noclose 1454 data = IO.popen("-", "r+") do |f| 1455 break f.read if f 1456 Process.daemon(true, true) 1457 puts "ok", Dir.pwd 1458 end 1459 assert_equal("ok\n#{Dir.pwd}\n", data) 1460 end 1461 1462 def test_daemon_readwrite 1463 data = IO.popen("-", "r+") do |f| 1464 if f 1465 f.puts "ok?" 1466 break f.read 1467 end 1468 Process.daemon(true, true) 1469 puts STDIN.gets 1470 end 1471 assert_equal("ok?\n", data) 1472 end 1473 1474 def test_daemon_pid 1475 cpid, dpid = IO.popen("-", "r+") do |f| 1476 break f.pid, Integer(f.read) if f 1477 Process.daemon(false, true) 1478 puts $$ 1479 end 1480 assert_not_equal(cpid, dpid) 1481 end 1482 1483 if File.directory?("/proc/self/task") && /netbsd[a-z]*[1-6]/ !~ RUBY_PLATFORM 1484 def test_daemon_no_threads 1485 pid, data = IO.popen("-", "r+") do |f| 1486 break f.pid, f.readlines if f 1487 Process.daemon(true, true) 1488 puts Dir.entries("/proc/self/task") - %W[. ..] 1489 end 1490 bug4920 = '[ruby-dev:43873]' 1491 assert_equal(2, data.size, bug4920) 1492 assert_not_include(data.map(&:to_i), pid) 1493 end 1494 else # darwin 1495 def test_daemon_no_threads 1496 data = Timeout.timeout(3) do 1497 IO.popen("-") do |f| 1498 break f.readlines.map(&:chomp) if f 1499 th = Thread.start {sleep 3} 1500 Process.daemon(true, true) 1501 puts Thread.list.size, th.status.inspect 1502 end 1503 end 1504 assert_equal(["1", "false"], data) 1505 end 1506 end 1507 end 1508 1509 def test_popen_cloexec 1510 return unless defined? Fcntl::FD_CLOEXEC 1511 IO.popen([RUBY, "-e", ""]) {|io| 1512 assert(io.close_on_exec?) 1513 } 1514 end 1515 1516 def test_execopts_new_pgroup 1517 return unless windows? 1518 1519 assert_nothing_raised { system(*TRUECOMMAND, :new_pgroup=>true) } 1520 assert_nothing_raised { system(*TRUECOMMAND, :new_pgroup=>false) } 1521 assert_nothing_raised { spawn(*TRUECOMMAND, :new_pgroup=>true) } 1522 assert_nothing_raised { IO.popen([*TRUECOMMAND, :new_pgroup=>true]) {} } 1523 end 1524 1525 def test_execopts_uid 1526 feature6975 = '[ruby-core:47414]' 1527 1528 [30000, [Process.uid, ENV["USER"]]].each do |uid, user| 1529 if user 1530 assert_nothing_raised(feature6975) do 1531 begin 1532 system(*TRUECOMMAND, uid: user) 1533 rescue Errno::EPERM, NotImplementedError 1534 end 1535 end 1536 end 1537 1538 assert_nothing_raised(feature6975) do 1539 begin 1540 system(*TRUECOMMAND, uid: uid) 1541 rescue Errno::EPERM, NotImplementedError 1542 end 1543 end 1544 1545 assert_nothing_raised(feature6975) do 1546 begin 1547 u = IO.popen([RUBY, "-e", "print Process.uid", uid: user||uid], &:read) 1548 assert_equal(uid.to_s, u, feature6975) 1549 rescue Errno::EPERM, NotImplementedError 1550 end 1551 end 1552 end 1553 end 1554 1555 def test_execopts_gid 1556 skip "Process.groups not implemented on Windows platform" if windows? 1557 feature6975 = '[ruby-core:47414]' 1558 1559 [30000, *Process.groups.map {|g| g = Etc.getgrgid(g); [g.name, g.gid]}].each do |group, gid| 1560 assert_nothing_raised(feature6975) do 1561 begin 1562 system(*TRUECOMMAND, gid: group) 1563 rescue Errno::EPERM, NotImplementedError 1564 end 1565 end 1566 1567 gid = "#{gid || group}" 1568 assert_nothing_raised(feature6975) do 1569 begin 1570 g = IO.popen([RUBY, "-e", "print Process.gid", gid: group], &:read) 1571 assert_equal(gid, g, feature6975) 1572 rescue Errno::EPERM, NotImplementedError 1573 end 1574 end 1575 end 1576 end 1577 1578 def test_sigpipe 1579 system(RUBY, "-e", "") 1580 with_pipe {|r, w| 1581 r.close 1582 assert_raise(Errno::EPIPE) { w.print "a" } 1583 } 1584 end 1585 1586 def test_sh_comment 1587 IO.popen("echo a # fofoof") {|f| 1588 assert_equal("a\n", f.read) 1589 } 1590 end if File.executable?("/bin/sh") 1591 1592 def test_sh_env 1593 IO.popen("foofoo=barbar env") {|f| 1594 lines = f.readlines 1595 assert_operator(lines, :include?, "foofoo=barbar\n") 1596 } 1597 end if File.executable?("/bin/sh") 1598 1599 def test_sh_exec 1600 IO.popen("exec echo exexexec") {|f| 1601 assert_equal("exexexec\n", f.read) 1602 } 1603 end if File.executable?("/bin/sh") 1604 1605 def test_setsid 1606 return unless Process.respond_to?(:setsid) 1607 return unless Process.respond_to?(:getsid) 1608 # OpenBSD doesn't allow Process::getsid(pid) when pid is in 1609 # different session. 1610 return if /openbsd/ =~ RUBY_PLATFORM 1611 1612 IO.popen([RUBY, "-e", <<EOS]) do|io| 1613 Marshal.dump(Process.getsid, STDOUT) 1614 newsid = Process.setsid 1615 Marshal.dump(newsid, STDOUT) 1616 STDOUT.flush 1617 # getsid() on MacOS X return ESRCH when target process is zombie 1618 # even if it is valid process id. 1619 sleep 1620EOS 1621 begin 1622 # test Process.getsid() w/o arg 1623 assert_equal(Marshal.load(io), Process.getsid) 1624 1625 # test Process.setsid return value and Process::getsid(pid) 1626 assert_equal(Marshal.load(io), Process.getsid(io.pid)) 1627 ensure 1628 Process.kill(:KILL, io.pid) rescue nil 1629 Process.wait(io.pid) 1630 end 1631 end 1632 end 1633 1634 1635 1636end 1637