* io.c (rb_open_file): encoding in mode string was ignored if perm is
[ruby-svn.git] / test / ruby / test_process.rb
blob668d05474360b79fb3c61bb21148b1967cfb9ae5
1 require 'test/unit'
2 require 'tmpdir'
3 require 'pathname'
4 require_relative 'envutil'
6 class TestProcess < Test::Unit::TestCase
7   RUBY = EnvUtil.rubybin
9   def setup
10     Process.waitall
11   end
13   def teardown
14     Process.waitall
15   end
17   def write_file(filename, content)
18     File.open(filename, "w") {|f|
19       f << content
20     }
21   end
23   def with_tmpchdir
24     Dir.mktmpdir {|d|
25       d = Pathname.new(d).realpath.to_s
26       Dir.chdir(d) {
27         yield d
28       }
29     }
30   end
32   def run_in_child(str) # should be called in a temporary directory
33     write_file("test-script", str)
34     Process.wait spawn(RUBY, "test-script")
35     $?
36   end
38   def test_rlimit_availability
39     begin
40       Process.getrlimit(nil)
41     rescue NotImplementedError
42       assert_raise(NotImplementedError) { Process.setrlimit }
43     rescue TypeError
44       assert_raise(ArgumentError) { Process.setrlimit }
45     end
46   end
48   def rlimit_exist?
49     Process.getrlimit(nil)
50   rescue NotImplementedError
51     return false
52   rescue TypeError
53     return true
54   end
56   def test_rlimit_nofile
57     return unless rlimit_exist?
58     with_tmpchdir {
59       write_file 's', <<-"End"
60         cur_nofile, max_nofile = Process.getrlimit(Process::RLIMIT_NOFILE)
61         result = 1
62         begin
63           Process.setrlimit(Process::RLIMIT_NOFILE, 0, max_nofile)
64         rescue Errno::EINVAL
65           result = 0
66         end
67         if result == 1
68           begin
69             IO.pipe
70           rescue Errno::EMFILE
71            result = 0
72           end
73         end
74         Process.setrlimit(Process::RLIMIT_NOFILE, cur_nofile, max_nofile)
75         exit result
76       End
77       pid = spawn RUBY, "s"
78       Process.wait pid
79       assert_equal(0, $?.to_i, "#{$?}")
80     }
81   end
83   def test_rlimit_name
84     return unless rlimit_exist?
85     [
86       :AS, "AS",
87       :CORE, "CORE",
88       :CPU, "CPU",
89       :DATA, "DATA",
90       :FSIZE, "FSIZE",
91       :MEMLOCK, "MEMLOCK",
92       :NOFILE, "NOFILE",
93       :NPROC, "NPROC",
94       :RSS, "RSS",
95       :STACK, "STACK",
96       :SBSIZE, "SBSIZE",
97     ].each {|name|
98       if Process.const_defined? "RLIMIT_#{name}"
99         assert_nothing_raised { Process.getrlimit(name) }
100       else
101         assert_raise(ArgumentError) { Process.getrlimit(name) }
102       end
103     }
104     assert_raise(ArgumentError) { Process.getrlimit(:FOO) }
105     assert_raise(ArgumentError) { Process.getrlimit("FOO") }
106   end
108   def test_rlimit_value
109     return unless rlimit_exist?
110     assert_raise(ArgumentError) { Process.setrlimit(:CORE, :FOO) }
111     assert_raise(Errno::EPERM) { Process.setrlimit(:NOFILE, :INFINITY) }
112     assert_raise(Errno::EPERM) { Process.setrlimit(:NOFILE, "INFINITY") }
113   end
115   TRUECOMMAND = [RUBY, '-e', '']
117   def test_execopts_opts
118     assert_nothing_raised {
119       Process.wait Process.spawn(*TRUECOMMAND, {})
120     }
121     assert_raise(ArgumentError) {
122       Process.wait Process.spawn(*TRUECOMMAND, :foo => 100)
123     }
124     assert_raise(ArgumentError) {
125       Process.wait Process.spawn(*TRUECOMMAND, Process => 100)
126     }
127   end
129   def test_execopts_pgroup
130     assert_nothing_raised { system(*TRUECOMMAND, :pgroup=>false) }
132     io = IO.popen([RUBY, "-e", "print Process.getpgrp"])
133     assert_equal(Process.getpgrp.to_s, io.read)
134     io.close
136     io = IO.popen([RUBY, "-e", "print Process.getpgrp", :pgroup=>true])
137     assert_equal(io.pid.to_s, io.read)
138     io.close
140     assert_raise(ArgumentError) { system(*TRUECOMMAND, :pgroup=>-1) }
141     assert_raise(Errno::EPERM) { Process.wait spawn(*TRUECOMMAND, :pgroup=>2) }
143     io1 = IO.popen([RUBY, "-e", "print Process.getpgrp", :pgroup=>true])
144     io2 = IO.popen([RUBY, "-e", "print Process.getpgrp", :pgroup=>io1.pid])
145     assert_equal(io1.pid.to_s, io1.read)
146     assert_equal(io1.pid.to_s, io2.read)
147     Process.wait io1.pid
148     Process.wait io2.pid
149     io1.close
150     io2.close
151   end
153   def test_execopts_rlimit
154     return unless rlimit_exist?
155     assert_raise(ArgumentError) { system(*TRUECOMMAND, :rlimit_foo=>0) }
156     assert_raise(ArgumentError) { system(*TRUECOMMAND, :rlimit_NOFILE=>0) }
157     assert_raise(ArgumentError) { system(*TRUECOMMAND, :rlimit_nofile=>[]) }
158     assert_raise(ArgumentError) { system(*TRUECOMMAND, :rlimit_nofile=>[1,2,3]) }
160     max = Process.getrlimit(:CORE).last
162     n = max
163     IO.popen([RUBY, "-e",
164              "p Process.getrlimit(:CORE)", :rlimit_core=>n]) {|io|
165       assert_equal("[#{n}, #{n}]\n", io.read)
166     }
168     n = 0
169     IO.popen([RUBY, "-e",
170              "p Process.getrlimit(:CORE)", :rlimit_core=>n]) {|io|
171       assert_equal("[#{n}, #{n}]\n", io.read)
172     }
174     n = max
175     IO.popen([RUBY, "-e",
176              "p Process.getrlimit(:CORE)", :rlimit_core=>[n]]) {|io|
177       assert_equal("[#{n}, #{n}]", io.read.chomp)
178     }
180     m, n = 0, max
181     IO.popen([RUBY, "-e",
182              "p Process.getrlimit(:CORE)", :rlimit_core=>[m,n]]) {|io|
183       assert_equal("[#{m}, #{n}]", io.read.chomp)
184     }
186     m, n = 0, 0
187     IO.popen([RUBY, "-e",
188              "p Process.getrlimit(:CORE)", :rlimit_core=>[m,n]]) {|io|
189       assert_equal("[#{m}, #{n}]", io.read.chomp)
190     }
192     n = max
193     IO.popen([RUBY, "-e",
194       "p Process.getrlimit(:CORE), Process.getrlimit(:CPU)",
195       :rlimit_core=>n, :rlimit_cpu=>3600]) {|io|
196       assert_equal("[#{n}, #{n}]\n[3600, 3600]", io.read.chomp)
197     }
198   end
200   ENVCOMMAND = [RUBY, '-e', 'ENV.each {|k,v| puts "#{k}=#{v}" }']
202   def test_execopts_env
203     assert_raise(ArgumentError) {
204       system({"F=O"=>"BAR"}, *TRUECOMMAND)
205     }
207     h = {}
208     ENV.each {|k,v| h[k] = nil unless k.upcase == "PATH" }
209     IO.popen([h, RUBY, '-e', 'puts ENV.keys.map{|e|e.upcase}']) {|io|
210       assert_equal("PATH\n", io.read)
211     }
213     IO.popen([{"FOO"=>"BAR"}, *ENVCOMMAND]) {|io|
214       assert_match(/^FOO=BAR$/, io.read)
215     }
217     with_tmpchdir {|d|
218       system({"fofo"=>"haha"}, *ENVCOMMAND, STDOUT=>"out")
219       assert_match(/^fofo=haha$/, File.read("out").chomp)
220     }
221   end
223   def test_execopts_unsetenv_others
224     IO.popen([*ENVCOMMAND, :unsetenv_others=>true]) {|io|
225       assert_equal("", io.read)
226     }
227     IO.popen([{"A"=>"B"}, *ENVCOMMAND, :unsetenv_others=>true]) {|io|
228       assert_equal("A=B\n", io.read)
229     }
230   end
232   PWD = [RUBY, '-e', 'puts Dir.pwd']
234   def test_execopts_chdir
235     with_tmpchdir {|d|
236       IO.popen([*PWD, :chdir => d]) {|io|
237         assert_equal(d, io.read.chomp)
238       }
239       assert_raise(Errno::ENOENT) {
240         Process.wait Process.spawn(*PWD, :chdir => "d/notexist")
241       }
242     }
243   end
245   UMASK = [RUBY, '-e', 'printf "%04o\n", File.umask']
247   def test_execopts_umask
248     IO.popen([*UMASK, :umask => 0]) {|io|
249       assert_equal("0000", io.read.chomp)
250     }
251     IO.popen([*UMASK, :umask => 0777]) {|io|
252       assert_equal("0777", io.read.chomp)
253     }
254   end
256   def with_pipe
257     begin
258       r, w = IO.pipe
259       yield r, w
260     ensure
261       r.close unless r.closed?
262       w.close unless w.closed?
263     end
264   end
266   def with_pipes(n)
267     ary = []
268     begin
269       n.times {
270         ary << IO.pipe
271       }
272       yield ary
273     ensure
274       ary.each {|r, w|
275         r.close unless r.closed?
276         w.close unless w.closed?
277       }
278     end
279   end
281   ECHO = lambda {|arg| [RUBY, '-e', "puts #{arg.dump}; STDOUT.flush"] }
282   SORT = [RUBY, '-e', "puts ARGF.readlines.sort"]
283   CAT = [RUBY, '-e', "IO.copy_stream STDIN, STDOUT"]
285   def test_execopts_redirect
286     with_tmpchdir {|d|
287       Process.wait Process.spawn(*ECHO["a"], STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644])
288       assert_equal("a", File.read("out").chomp)
289       Process.wait Process.spawn(*ECHO["0"], STDOUT=>["out", File::WRONLY|File::CREAT|File::APPEND, 0644])
290       assert_equal("a\n0\n", File.read("out"))
291       Process.wait Process.spawn(*SORT, STDIN=>["out", File::RDONLY, 0644],
292                                          STDOUT=>["out2", File::WRONLY|File::CREAT|File::TRUNC, 0644])
293       assert_equal("0\na\n", File.read("out2"))
294       Process.wait Process.spawn(*ECHO["b"], [STDOUT, STDERR]=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644])
295       assert_equal("b", File.read("out").chomp)
296       # problem occur with valgrind
297       #Process.wait Process.spawn(*ECHO["a"], STDOUT=>:close, STDERR=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644])
298       #p File.read("out")
299       #assert(!File.read("out").empty?) # error message such as "-e:1:in `flush': Bad file descriptor (Errno::EBADF)"
300       Process.wait Process.spawn(*ECHO["c"], STDERR=>STDOUT, STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644])
301       assert_equal("c", File.read("out").chomp)
302       File.open("out", "w") {|f|
303         Process.wait Process.spawn(*ECHO["d"], f=>STDOUT, STDOUT=>f)
304         assert_equal("d", File.read("out").chomp)
305       }
306       Process.wait Process.spawn(*ECHO["e"], STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644],
307                                  3=>STDOUT, 4=>STDOUT, 5=>STDOUT, 6=>STDOUT, 7=>STDOUT)
308       assert_equal("e", File.read("out").chomp)
309       File.open("out", "w") {|f|
310         h = {STDOUT=>f, f=>STDOUT}
311         3.upto(30) {|i| h[i] = STDOUT if f.fileno != i }
312         Process.wait Process.spawn(*ECHO["f"], h)
313         assert_equal("f", File.read("out").chomp)
314       }
315       assert_raise(ArgumentError) {
316         Process.wait Process.spawn(*ECHO["f"], 1=>Process)
317       }
318       assert_raise(ArgumentError) {
319         Process.wait Process.spawn(*ECHO["f"], [Process]=>1)
320       }
321       assert_raise(ArgumentError) {
322         Process.wait Process.spawn(*ECHO["f"], [1, STDOUT]=>2)
323       }
324       assert_raise(ArgumentError) {
325         Process.wait Process.spawn(*ECHO["f"], -1=>2)
326       }
327       Process.wait Process.spawn(*ECHO["hhh\nggg\n"], STDOUT=>"out")
328       assert_equal("hhh\nggg\n", File.read("out"))
329       Process.wait Process.spawn(*SORT, STDIN=>"out", STDOUT=>"out2")
330       assert_equal("ggg\nhhh\n", File.read("out2"))
332       assert_raise(Errno::ENOENT) {
333         Process.wait Process.spawn("non-existing-command", (3..60).to_a=>["err", File::WRONLY|File::CREAT])
334       }
335       assert_equal("", File.read("err"))
337       system(*ECHO["bb\naa\n"], STDOUT=>["out", "w"])
338       assert_equal("bb\naa\n", File.read("out"))
339       system(*SORT, STDIN=>["out"], STDOUT=>"out2")
340       assert_equal("aa\nbb\n", File.read("out2"))
342       with_pipe {|r1, w1|
343         with_pipe {|r2, w2|
344           pid = spawn(*SORT, STDIN=>r1, STDOUT=>w2, w1=>:close, r2=>:close)
345           r1.close
346           w2.close
347           w1.puts "c"
348           w1.puts "a"
349           w1.puts "b"
350           w1.close
351           assert_equal("a\nb\nc\n", r2.read)
352           Process.wait(pid)
353         }
354       }
356       with_pipes(5) {|pipes|
357         ios = pipes.flatten
358         h = {}
359         ios.length.times {|i| h[ios[i]] = ios[(i-1)%ios.length] }
360         h2 = h.invert
361         rios = pipes.map {|r, w| r }
362         wios = pipes.map {|r, w| w }
363         child_wfds = wios.map {|w| h2[w].fileno }
364         pid = spawn(RUBY, "-e",
365                 "[#{child_wfds.join(',')}].each {|fd| IO.new(fd).puts fd }", h)
366         pipes.each {|r, w|
367           assert_equal("#{h2[w].fileno}\n", r.gets)
368         }
369         Process.wait pid;
370       }
372       with_pipes(5) {|pipes|
373         ios = pipes.flatten
374         h = {}
375         ios.length.times {|i| h[ios[i]] = ios[(i+1)%ios.length] }
376         h2 = h.invert
377         rios = pipes.map {|r, w| r }
378         wios = pipes.map {|r, w| w }
379         child_wfds = wios.map {|w| h2[w].fileno }
380         pid = spawn(RUBY, "-e",
381                 "[#{child_wfds.join(',')}].each {|fd| IO.new(fd).puts fd }", h)
382         pipes.each {|r, w|
383           assert_equal("#{h2[w].fileno}\n", r.gets)
384         }
385         Process.wait pid;
386       }
388       closed_fd = nil
389       with_pipes(5) {|pipes|
390         io = pipes.last.last
391         closed_fd = io.fileno
392       }
393       assert_raise(Errno::EBADF) { Process.wait spawn(*TRUECOMMAND, closed_fd=>closed_fd) }
395       with_pipe {|r, w|
396         w.close_on_exec = true
397         pid = spawn(RUBY, "-e", "IO.new(#{w.fileno}).print 'a'", w=>w)
398         w.close
399         assert_equal("a", r.read)
400         Process.wait pid
401       }
403       system(*ECHO["funya"], :out=>"out")
404       assert_equal("funya\n", File.read("out"))
405       system(RUBY, '-e', 'STDOUT.reopen(STDERR); puts "henya"', :err=>"out")
406       assert_equal("henya\n", File.read("out"))
407       IO.popen([*CAT, :in=>"out"]) {|io|
408         assert_equal("henya\n", io.read)
409       }
410     }
411   end
413   def test_execopts_exec
414     with_tmpchdir {|d|
415       write_file("s", 'exec "echo aaa", STDOUT=>"foo"')
416       pid = spawn RUBY, 's'
417       Process.wait pid
418       assert_equal("aaa\n", File.read("foo"))
419     }
420   end
422   def test_execopts_popen
423     with_tmpchdir {|d|
424       IO.popen("#{RUBY} -e 'puts :foo'") {|io| assert_equal("foo\n", io.read) }
425       assert_raise(Errno::ENOENT) { IO.popen(["echo bar"]) {} } # assuming "echo bar" command not exist.
426       IO.popen(ECHO["baz"]) {|io| assert_equal("baz\n", io.read) }
427       assert_raise(ArgumentError) {
428         IO.popen([*ECHO["qux"], STDOUT=>STDOUT]) {|io| }
429       }
430       IO.popen([*ECHO["hoge"], STDERR=>STDOUT]) {|io|
431         assert_equal("hoge\n", io.read)
432       }
433       assert_raise(ArgumentError) {
434         IO.popen([*ECHO["fuga"], STDOUT=>"out"]) {|io| }
435       }
436       with_pipe {|r, w|
437         IO.popen([RUBY, '-e', 'IO.new(3).puts("a"); puts "b"', 3=>w]) {|io|
438           assert_equal("b\n", io.read)
439         }
440         w.close
441         assert_equal("a\n", r.read)
442       }
443       IO.popen([RUBY, '-e', "IO.new(9).puts(:b)",
444                9=>["out2", File::WRONLY|File::CREAT|File::TRUNC]]) {|io|
445         assert_equal("", io.read)
446       }
447       assert_equal("b\n", File.read("out2"))
448     }
449   end
451   def test_popen_fork
452     return if /freebsd/ =~ RUBY_PLATFORM # this test freeze in FreeBSD
453     IO.popen("-") {|io|
454       if !io
455         puts "fooo"
456       else
457         assert_equal("fooo\n", io.read)
458       end
459     }
460   rescue NotImplementedError
461   end
463   def test_fd_inheritance
464     with_pipe {|r, w|
465       system(RUBY, '-e', 'IO.new(ARGV[0].to_i).puts(:ba)', w.fileno.to_s)
466       w.close
467       assert_equal("ba\n", r.read)
468     }
469     with_pipe {|r, w|
470       Process.wait spawn(RUBY, '-e',
471                          'IO.new(ARGV[0].to_i).puts("bi") rescue nil',
472                          w.fileno.to_s)
473       w.close
474       assert_equal("", r.read)
475     }
476     with_pipe {|r, w|
477       with_tmpchdir {|d|
478         write_file("s", <<-"End")
479           exec(#{RUBY.dump}, '-e',
480                'IO.new(ARGV[0].to_i).puts("bu") rescue nil',
481                #{w.fileno.to_s.dump})
482         End
483         Process.wait spawn(RUBY, "s", :close_others=>false)
484         w.close
485         assert_equal("bu\n", r.read)
486       }
487     }
488     with_pipe {|r, w|
489       io = IO.popen([RUBY, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}).puts('me')"])
490       w.close
491       errmsg = io.read
492       assert_equal("", r.read)
493       assert_not_equal("", errmsg)
494       Process.wait
495     }
496     with_pipe {|r, w|
497       errmsg = `#{RUBY} -e "STDERR.reopen(STDOUT); IO.new(#{w.fileno}).puts(123)"`
498       w.close
499       assert_equal("", r.read)
500       assert_not_equal("", errmsg)
501     }
502   end
504   def test_execopts_close_others
505     with_tmpchdir {|d|
506       with_pipe {|r, w|
507         system(RUBY, '-e', 'STDERR.reopen("err", "w"); IO.new(ARGV[0].to_i).puts("ma")', w.fileno.to_s, :close_others=>true)
508         w.close
509         assert_equal("", r.read)
510         assert_not_equal("", File.read("err"))
511         File.unlink("err")
512       }
513       with_pipe {|r, w|
514         Process.wait spawn(RUBY, '-e', 'STDERR.reopen("err", "w"); IO.new(ARGV[0].to_i).puts("mi")', w.fileno.to_s, :close_others=>true)
515         w.close
516         assert_equal("", r.read)
517         assert_not_equal("", File.read("err"))
518         File.unlink("err")
519       }
520       with_pipe {|r, w|
521         Process.wait spawn(RUBY, '-e', 'IO.new(ARGV[0].to_i).puts("bi")', w.fileno.to_s, :close_others=>false)
522         w.close
523         assert_equal("bi\n", r.read)
524       }
525       with_pipe {|r, w|
526         write_file("s", <<-"End")
527           exec(#{RUBY.dump}, '-e',
528                'STDERR.reopen("err", "w"); IO.new(ARGV[0].to_i).puts("mu")',
529                #{w.fileno.to_s.dump},
530                :close_others=>true)
531         End
532         Process.wait spawn(RUBY, "s", :close_others=>false)
533         w.close
534         assert_equal("", r.read)
535         assert_not_equal("", File.read("err"))
536         File.unlink("err")
537       }
538       with_pipe {|r, w|
539         io = IO.popen([RUBY, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}).puts('me')", :close_others=>true])
540         w.close
541         errmsg = io.read
542         assert_equal("", r.read)
543         assert_not_equal("", errmsg)
544         Process.wait
545       }
546       with_pipe {|r, w|
547         io = IO.popen([RUBY, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}).puts('mo')", :close_others=>false])
548         w.close
549         errmsg = io.read
550         assert_equal("mo\n", r.read)
551         assert_equal("", errmsg)
552         Process.wait
553       }
554       with_pipe {|r, w|
555         io = IO.popen([RUBY, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}).puts('mo')", :close_others=>nil])
556         w.close
557         errmsg = io.read
558         assert_equal("mo\n", r.read)
559         assert_equal("", errmsg)
560         Process.wait
561       }
563     }
564   end
566   def test_execopts_redirect_self
567     with_pipe {|r, w|
568       w << "haha\n"
569       w.close
570       r.close_on_exec = true
571       IO.popen([RUBY, "-e", "print IO.new(#{r.fileno}).read", r.fileno=>r.fileno, :close_others=>false]) {|io|
572         assert_equal("haha\n", io.read)
573       }
574     }
575   end
577   def test_execopts_duplex_io
578     IO.popen("#{RUBY} -e ''", "r+") {|duplex|
579       assert_raise(ArgumentError) { system("#{RUBY} -e ''", duplex=>STDOUT) }
580       assert_raise(ArgumentError) { system("#{RUBY} -e ''", STDOUT=>duplex) }
581     }
582   end
584   def test_execopts_modification
585     h = {}
586     Process.wait spawn(*TRUECOMMAND, h)
587     assert_equal({}, h)
589     h = {}
590     system(*TRUECOMMAND, h)
591     assert_equal({}, h)
593     h = {}
594     io = IO.popen([*TRUECOMMAND, h])
595     io.close
596     assert_equal({}, h)
597   end
599   def test_system_noshell
600     str = "echo non existing command name which contains spaces"
601     assert_nil(system([str, str]))
602   end
604   def test_spawn_noshell
605     str = "echo non existing command name which contains spaces"
606     assert_raise(Errno::ENOENT) { spawn([str, str]) }
607   end
609   def test_popen_noshell
610     str = "echo non existing command name which contains spaces"
611     assert_raise(Errno::ENOENT) { IO.popen([str, str]) }
612   end
614   def test_exec_noshell
615     with_tmpchdir {|d|
616       with_pipe {|r, w|
617         write_file("s", <<-"End")
618           str = "echo non existing command name which contains spaces"
619           w = IO.new(#{w.fileno})
620           STDOUT.reopen(w)
621           STDERR.reopen(w)
622           begin
623             exec [str, str]
624           rescue Errno::ENOENT
625             w.write "Errno::ENOENT success"
626           end
627         End
628         system(RUBY, "s", :close_others=>false)
629         w.close
630         assert_equal("Errno::ENOENT success", r.read)
631       }
632     }
633   end
635   def test_system_wordsplit
636     with_tmpchdir {|d|
637       write_file("script", <<-'End')
638         File.open("result", "w") {|t| t << "haha pid=#{$$} ppid=#{Process.ppid}" }
639         exit 5
640       End
641       str = "#{RUBY} script"
642       ret = system(str)
643       status = $?
644       assert_equal(false, ret)
645       assert(status.exited?)
646       assert_equal(5, status.exitstatus)
647       assert_equal("haha pid=#{status.pid} ppid=#{$$}", File.read("result"))
648     }
649   end
651   def test_spawn_wordsplit
652     with_tmpchdir {|d|
653       write_file("script", <<-'End')
654         File.open("result", "w") {|t| t << "hihi pid=#{$$} ppid=#{Process.ppid}" }
655         exit 6
656       End
657       str = "#{RUBY} script"
658       pid = spawn(str)
659       Process.wait pid
660       status = $?
661       assert_equal(pid, status.pid)
662       assert(status.exited?)
663       assert_equal(6, status.exitstatus)
664       assert_equal("hihi pid=#{status.pid} ppid=#{$$}", File.read("result"))
665     }
666   end
668   def test_popen_wordsplit
669     with_tmpchdir {|d|
670       write_file("script", <<-'End')
671         print "fufu pid=#{$$} ppid=#{Process.ppid}"
672         exit 7
673       End
674       str = "#{RUBY} script"
675       io = IO.popen(str)
676       pid = io.pid
677       result = io.read
678       io.close
679       status = $?
680       assert_equal(pid, status.pid)
681       assert(status.exited?)
682       assert_equal(7, status.exitstatus)
683       assert_equal("fufu pid=#{status.pid} ppid=#{$$}", result)
684     }
685   end
687   def test_exec_wordsplit
688     with_tmpchdir {|d|
689       write_file("script", <<-'End')
690         File.open("result", "w") {|t|
691           if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
692             t << "hehe ppid=#{Process.ppid}"
693           else
694             t << "hehe pid=#{$$} ppid=#{Process.ppid}"
695           end
696         }
697         exit 6
698       End
699       write_file("s", <<-"End")
700         ruby = #{RUBY.dump}
701         exec "\#{ruby} script"
702       End
703       pid = spawn(RUBY, "s")
704       Process.wait pid
705       status = $?
706       assert_equal(pid, status.pid)
707       assert(status.exited?)
708       assert_equal(6, status.exitstatus)
709       if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
710         expected = "hehe ppid=#{status.pid}"
711       else
712         expected = "hehe pid=#{status.pid} ppid=#{$$}"
713       end
714       assert_equal(expected, File.read("result"))
715     }
716   end
718   def test_system_shell
719     with_tmpchdir {|d|
720       write_file("script1", <<-'End')
721         File.open("result1", "w") {|t| t << "taka pid=#{$$} ppid=#{Process.ppid}" }
722         exit 7
723       End
724       write_file("script2", <<-'End')
725         File.open("result2", "w") {|t| t << "taki pid=#{$$} ppid=#{Process.ppid}" }
726         exit 8
727       End
728       ret = system("#{RUBY} script1 || #{RUBY} script2")
729       status = $?
730       assert_equal(false, ret)
731       assert(status.exited?)
732       result1 = File.read("result1")
733       result2 = File.read("result2")
734       assert_match(/\Ataka pid=\d+ ppid=\d+\z/, result1)
735       assert_match(/\Ataki pid=\d+ ppid=\d+\z/, result2)
736       assert_not_equal(result1[/\d+/].to_i, status.pid)
737     }
738   end
740   def test_spawn_shell
741     with_tmpchdir {|d|
742       write_file("script1", <<-'End')
743         File.open("result1", "w") {|t| t << "taku pid=#{$$} ppid=#{Process.ppid}" }
744         exit 7
745       End
746       write_file("script2", <<-'End')
747         File.open("result2", "w") {|t| t << "take pid=#{$$} ppid=#{Process.ppid}" }
748         exit 8
749       End
750       pid = spawn("#{RUBY} script1 || #{RUBY} script2")
751       Process.wait pid
752       status = $?
753       assert(status.exited?)
754       assert(!status.success?)
755       result1 = File.read("result1")
756       result2 = File.read("result2")
757       assert_match(/\Ataku pid=\d+ ppid=\d+\z/, result1)
758       assert_match(/\Atake pid=\d+ ppid=\d+\z/, result2)
759       assert_not_equal(result1[/\d+/].to_i, status.pid)
760     }
761   end
763   def test_popen_shell
764     with_tmpchdir {|d|
765       write_file("script1", <<-'End')
766         puts "tako pid=#{$$} ppid=#{Process.ppid}"
767         exit 7
768       End
769       write_file("script2", <<-'End')
770         puts "tika pid=#{$$} ppid=#{Process.ppid}"
771         exit 8
772       End
773       io = IO.popen("#{RUBY} script1 || #{RUBY} script2")
774       result = io.read
775       io.close
776       status = $?
777       assert(status.exited?)
778       assert(!status.success?)
779       assert_match(/\Atako pid=\d+ ppid=\d+\ntika pid=\d+ ppid=\d+\n\z/, result)
780       assert_not_equal(result[/\d+/].to_i, status.pid)
781     }
782   end
784   def test_exec_shell
785     with_tmpchdir {|d|
786       write_file("script1", <<-'End')
787         File.open("result1", "w") {|t| t << "tiki pid=#{$$} ppid=#{Process.ppid}" }
788         exit 7
789       End
790       write_file("script2", <<-'End')
791         File.open("result2", "w") {|t| t << "tiku pid=#{$$} ppid=#{Process.ppid}" }
792         exit 8
793       End
794       write_file("s", <<-"End")
795         ruby = #{RUBY.dump}
796         exec("\#{ruby} script1 || \#{ruby} script2")
797       End
798       pid = spawn RUBY, "s"
799       Process.wait pid
800       status = $?
801       assert(status.exited?)
802       assert(!status.success?)
803       result1 = File.read("result1")
804       result2 = File.read("result2")
805       assert_match(/\Atiki pid=\d+ ppid=\d+\z/, result1)
806       assert_match(/\Atiku pid=\d+ ppid=\d+\z/, result2)
807       assert_not_equal(result1[/\d+/].to_i, status.pid)
808     }
809   end
811   def test_argv0
812     with_tmpchdir {|d|
813       assert_equal(false, system([RUBY, "asdfg"], "-e", "exit false"))
814       assert_equal(true, system([RUBY, "zxcvb"], "-e", "exit true"))
816       Process.wait spawn([RUBY, "poiu"], "-e", "exit 4")
817       assert_equal(4, $?.exitstatus)
819       assert_equal("1", IO.popen([[RUBY, "qwerty"], "-e", "print 1"]).read)
820       Process.wait
822       write_file("s", <<-"End")
823         exec([#{RUBY.dump}, "lkjh"], "-e", "exit 5")
824       End
825       pid = spawn RUBY, "s"
826       Process.wait pid
827       assert_equal(5, $?.exitstatus)
828     }
829   end
831   def with_stdin(filename)
832     open(filename) {|f|
833       begin
834         old = STDIN.dup
835         begin
836           STDIN.reopen(filename)
837           yield
838         ensure
839           STDIN.reopen(old)
840         end
841       ensure
842         old.close
843       end
844     }
845   end
847   def test_argv0_noarg
848     with_tmpchdir {|d|
849       open("t", "w") {|f| f.print "exit true" }
850       open("f", "w") {|f| f.print "exit false" }
852       with_stdin("t") { assert_equal(true, system([RUBY, "qaz"])) }
853       with_stdin("f") { assert_equal(false, system([RUBY, "wsx"])) }
855       with_stdin("t") { Process.wait spawn([RUBY, "edc"]) }
856       assert($?.success?)
857       with_stdin("f") { Process.wait spawn([RUBY, "rfv"]) }
858       assert(!$?.success?)
860       with_stdin("t") { IO.popen([[RUBY, "tgb"]]) {|io| assert_equal("", io.read) } }
861       assert($?.success?)
862       with_stdin("f") { IO.popen([[RUBY, "yhn"]]) {|io| assert_equal("", io.read) } }
863       assert(!$?.success?)
865       status = run_in_child "STDIN.reopen('t'); exec([#{RUBY.dump}, 'ujm'])"
866       assert(status.success?)
867       status = run_in_child "STDIN.reopen('f'); exec([#{RUBY.dump}, 'ik,'])"
868       assert(!status.success?)
869     }
870   end
872   def test_status
873     with_tmpchdir do
874       s = run_in_child("exit 1")
875       assert_equal("#<Process::Status: pid #{ s.pid } exit #{ s.exitstatus }>", s.inspect)
877       assert_equal(s, s)
878       assert_equal(s, s.to_i)
880       assert_equal(s.to_i & 0x55555555, s & 0x55555555)
881       assert_equal(s.to_i >> 1, s >> 1)
882       assert_equal(false, s.stopped?)
883       assert_equal(nil, s.stopsig)
884     end
885   end
887   def test_status_kill
888     return unless Process.respond_to?(:kill)
889     return unless Signal.list.include?("QUIT")
891     with_tmpchdir do
892       write_file("foo", "sleep 30")
893       pid = spawn(RUBY, "foo")
894       Thread.new { sleep 1; Process.kill(:SIGQUIT, pid) }
895       Process.wait(pid)
896       s = $?
897       assert_send(
898         [["#<Process::Status: pid #{ s.pid } SIGQUIT (signal #{ s.termsig })>",
899           "#<Process::Status: pid #{ s.pid } SIGQUIT (signal #{ s.termsig }) (core dumped)>"],
900          :include?,
901          s.inspect])
902       assert_equal(false, s.exited?)
903       assert_equal(nil, s.success?)
904     end
905   end
907   def test_wait_without_arg
908     with_tmpchdir do
909       write_file("foo", "sleep 0.1")
910       pid = spawn(RUBY, "foo")
911       assert_equal(pid, Process.wait)
912     end
913   end
915   def test_wait2
916     with_tmpchdir do
917       write_file("foo", "sleep 0.1")
918       pid = spawn(RUBY, "foo")
919       assert_equal([pid, 0], Process.wait2)
920     end
921   end
923   def test_waitall
924     with_tmpchdir do
925       write_file("foo", "sleep 0.1")
926       ps = (0...3).map { spawn(RUBY, "foo") }.sort
927       ss = Process.waitall.sort
928       ps.zip(ss) do |p1, (p2, s)|
929         assert_equal(p1, p2)
930         assert_equal(p1, s.pid)
931       end
932     end
933   end
935   def test_abort
936     with_tmpchdir do
937       s = run_in_child("abort")
938       assert_not_equal(0, s.exitstatus)
939     end
940   end
942   def test_sleep
943     assert_raise(ArgumentError) { sleep(1, 1) }
944   end
946   def test_getpgid
947     assert_kind_of(Integer, Process.getpgid(Process.ppid))
948   rescue NotImplementedError
949   end
951   def test_getpriority
952     assert_kind_of(Integer, Process.getpriority(Process::PRIO_PROCESS, $$))
953   rescue NameError, NotImplementedError
954   end
956   def test_setpriority
957     if defined? Process::PRIO_USER
958       assert_nothing_raised do
959         pr = Process.getpriority(Process::PRIO_PROCESS, $$)
960         Process.setpriority(Process::PRIO_PROCESS, $$, pr)
961       end
962     end
963   end
965   def test_getuid
966     assert_kind_of(Integer, Process.uid)
967   end
969   def test_groups
970     gs = Process.groups
971     assert_instance_of(Array, gs)
972     gs.each {|g| assert_kind_of(Integer, g) }
973   rescue NotImplementedError
974   end
976   def test_maxgroups
977     assert_kind_of(Integer, Process.maxgroups)
978   end
980   def test_geteuid
981     assert_kind_of(Integer, Process.egid)
982   end
984   def test_uid_re_exchangeable_p
985     r = Process::UID.re_exchangeable?
986     assert(true == r || false == r)
987   end
989   def test_gid_re_exchangeable_p
990     r = Process::GID.re_exchangeable?
991     assert(true == r || false == r)
992   end
994   def test_uid_sid_available?
995     r = Process::UID.sid_available?
996     assert(true == r || false == r)
997   end
999   def test_gid_sid_available?
1000     r = Process::GID.sid_available?
1001     assert(true == r || false == r)
1002   end