1require 'test/unit' 2require 'timeout' 3require 'tempfile' 4require_relative 'envutil' 5 6class TestSignal < Test::Unit::TestCase 7 def have_fork? 8 begin 9 Process.fork {} 10 return true 11 rescue NotImplementedError 12 return false 13 end 14 end 15 16 def test_signal 17 return unless Process.respond_to?(:kill) 18 begin 19 x = 0 20 oldtrap = Signal.trap(:INT) {|sig| x = 2 } 21 Process.kill :INT, Process.pid 22 10.times do 23 break if 2 == x 24 sleep 0.1 25 end 26 assert_equal 2, x 27 28 Signal.trap(:INT) { raise "Interrupt" } 29 ex = assert_raise(RuntimeError) { 30 Process.kill :INT, Process.pid 31 sleep 0.1 32 } 33 assert_kind_of Exception, ex 34 assert_match(/Interrupt/, ex.message) 35 ensure 36 Signal.trap :INT, oldtrap if oldtrap 37 end 38 end 39 40 def test_signal_process_group 41 return unless Process.respond_to?(:kill) 42 return unless Process.respond_to?(:pgroup) # for mswin32 43 44 bug4362 = '[ruby-dev:43169]' 45 assert_nothing_raised(bug4362) do 46 pid = Process.spawn(EnvUtil.rubybin, '-e', 'sleep 10', :pgroup => true) 47 Process.kill(:"-TERM", pid) 48 Process.waitpid(pid) 49 assert_equal(true, $?.signaled?) 50 assert_equal(Signal.list["TERM"], $?.termsig) 51 end 52 end 53 54 def test_exit_action 55 return unless have_fork? # skip this test 56 begin 57 r, w = IO.pipe 58 r0, w0 = IO.pipe 59 pid = Process.spawn(EnvUtil.rubybin, '-e', <<-'End', 3=>w, 4=>r0) 60 w = IO.new(3, "w") 61 r0 = IO.new(4, "r") 62 Signal.trap(:USR1, "EXIT") 63 w.syswrite("a") 64 Thread.start { sleep(2) } 65 r0.sysread(4096) 66 End 67 r.sysread(1) 68 sleep 0.1 69 assert_nothing_raised("[ruby-dev:26128]") { 70 Process.kill(:USR1, pid) 71 begin 72 Timeout.timeout(3) { 73 Process.waitpid pid 74 } 75 rescue Timeout::Error 76 Process.kill(:TERM, pid) 77 raise 78 end 79 } 80 ensure 81 r.close 82 w.close 83 r0.close 84 w0.close 85 end 86 end 87 88 def test_invalid_signal_name 89 return unless Process.respond_to?(:kill) 90 91 assert_raise(ArgumentError) { Process.kill(:XXXXXXXXXX, $$) } 92 end 93 94 def test_signal_exception 95 assert_raise(ArgumentError) { SignalException.new } 96 assert_raise(ArgumentError) { SignalException.new(-1) } 97 assert_raise(ArgumentError) { SignalException.new(:XXXXXXXXXX) } 98 Signal.list.each do |signm, signo| 99 next if signm == "EXIT" 100 assert_equal(SignalException.new(signm).signo, signo) 101 assert_equal(SignalException.new(signm.to_sym).signo, signo) 102 assert_equal(SignalException.new(signo).signo, signo) 103 end 104 end 105 106 def test_interrupt 107 assert_raise(Interrupt) { raise Interrupt.new } 108 end 109 110 def test_signal2 111 return unless Process.respond_to?(:kill) 112 begin 113 x = false 114 oldtrap = Signal.trap(:INT) {|sig| x = true } 115 GC.start 116 117 assert_raise(ArgumentError) { Process.kill } 118 119 Timeout.timeout(10) do 120 x = false 121 Process.kill(SignalException.new(:INT).signo, $$) 122 sleep(0.01) until x 123 124 x = false 125 Process.kill("INT", $$) 126 sleep(0.01) until x 127 128 x = false 129 Process.kill("SIGINT", $$) 130 sleep(0.01) until x 131 132 x = false 133 o = Object.new 134 def o.to_str; "SIGINT"; end 135 Process.kill(o, $$) 136 sleep(0.01) until x 137 end 138 139 assert_raise(ArgumentError) { Process.kill(Object.new, $$) } 140 141 ensure 142 Signal.trap(:INT, oldtrap) if oldtrap 143 end 144 end 145 146 def test_trap 147 return unless Process.respond_to?(:kill) 148 begin 149 oldtrap = Signal.trap(:INT) {|sig| } 150 151 assert_raise(ArgumentError) { Signal.trap } 152 153 assert_raise(SecurityError) do 154 s = proc {}.taint 155 Signal.trap(:INT, s) 156 end 157 158 # FIXME! 159 Signal.trap(:INT, nil) 160 Signal.trap(:INT, "") 161 Signal.trap(:INT, "SIG_IGN") 162 Signal.trap(:INT, "IGNORE") 163 164 Signal.trap(:INT, "SIG_DFL") 165 Signal.trap(:INT, "SYSTEM_DEFAULT") 166 167 Signal.trap(:INT, "EXIT") 168 169 Signal.trap(:INT, "xxxxxx") 170 Signal.trap(:INT, "xxxx") 171 172 Signal.trap(SignalException.new(:INT).signo, "SIG_DFL") 173 174 assert_raise(ArgumentError) { Signal.trap(-1, "xxxx") } 175 176 o = Object.new 177 def o.to_str; "SIGINT"; end 178 Signal.trap(o, "SIG_DFL") 179 180 assert_raise(ArgumentError) { Signal.trap("XXXXXXXXXX", "SIG_DFL") } 181 182 ensure 183 Signal.trap(:INT, oldtrap) if oldtrap 184 end 185 end 186 187 def test_kill_immediately_before_termination 188 return unless have_fork? # skip this test 189 190 r, w = IO.pipe 191 pid = Process.fork do 192 r.close 193 Signal.trap(:USR1) { w.syswrite("foo") } 194 Process.kill :USR1, $$ 195 end 196 w.close 197 assert_equal(r.read, "foo") 198 end 199 200 def test_signal_requiring 201 t = Tempfile.new(%w"require_ensure_test .rb") 202 t.puts "sleep" 203 t.close 204 error = IO.popen([EnvUtil.rubybin, "-e", <<EOS, t.path, :err => File::NULL]) do |child| 205trap(:INT, "DEFAULT") 206th = Thread.new do 207 begin 208 require ARGV[0] 209 ensure 210 err = $! ? [$!, $!.backtrace] : $! 211 Marshal.dump(err, STDOUT) 212 STDOUT.flush 213 end 214end 215Thread.pass while th.running? 216Process.kill(:INT, $$) 217th.join 218EOS 219 Marshal.load(child) 220 end 221 t.close! 222 assert_nil(error) 223 end 224 225 def test_reserved_signal 226 assert_raise(ArgumentError) { 227 Signal.trap(:SEGV) {} 228 } 229 assert_raise(ArgumentError) { 230 Signal.trap(:BUS) {} 231 } 232 assert_raise(ArgumentError) { 233 Signal.trap(:ILL) {} 234 } 235 assert_raise(ArgumentError) { 236 Signal.trap(:FPE) {} 237 } 238 assert_raise(ArgumentError) { 239 Signal.trap(:VTALRM) {} 240 } 241 end 242 243 def test_signame 244 return unless Process.respond_to?(:kill) 245 246 10.times do 247 IO.popen([EnvUtil.rubybin, "-e", <<EOS, :err => File::NULL]) do |child| 248 Signal.trap("INT") do |signo| 249 signame = Signal.signame(signo) 250 Marshal.dump(signame, STDOUT) 251 STDOUT.flush 252 exit 0 253 end 254 Process.kill("INT", $$) 255 sleep 1 # wait signal deliver 256EOS 257 258 signame = Marshal.load(child) 259 assert_equal(signame, "INT") 260 end 261 end 262 end 263 264 def test_trap_puts 265 assert_in_out_err([], <<-INPUT, ["a"*10000], []) 266 Signal.trap(:INT) { 267 # for enable internal io mutex 268 STDOUT.sync = false 269 # larger than internal io buffer 270 print "a"*10000 271 } 272 Process.kill :INT, $$ 273 sleep 0.1 274 INPUT 275 end 276end 277