2003-09-04 12:18:59 -04:00
|
|
|
require 'test/unit'
|
2005-06-03 10:39:15 -04:00
|
|
|
require 'timeout'
|
2011-05-18 09:36:46 -04:00
|
|
|
require 'tempfile'
|
2010-08-09 09:12:54 -04:00
|
|
|
require_relative 'envutil'
|
2003-09-04 12:18:59 -04:00
|
|
|
|
|
|
|
class TestSignal < Test::Unit::TestCase
|
2005-09-15 09:42:15 -04:00
|
|
|
def have_fork?
|
|
|
|
begin
|
2006-12-31 10:02:22 -05:00
|
|
|
Process.fork {}
|
|
|
|
return true
|
2005-09-15 09:42:15 -04:00
|
|
|
rescue NotImplementedError
|
2006-12-31 10:02:22 -05:00
|
|
|
return false
|
2005-09-15 09:42:15 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2003-09-04 12:18:59 -04:00
|
|
|
def test_signal
|
2007-12-01 23:13:17 -05:00
|
|
|
return unless Process.respond_to?(:kill)
|
2003-10-19 22:31:47 -04:00
|
|
|
begin
|
2006-12-31 10:02:22 -05:00
|
|
|
x = 0
|
|
|
|
oldtrap = Signal.trap(:INT) {|sig| x = 2 }
|
|
|
|
Process.kill :INT, Process.pid
|
2009-10-18 07:19:33 -04:00
|
|
|
10.times do
|
|
|
|
break if 2 == x
|
|
|
|
sleep 0.1
|
|
|
|
end
|
2006-12-31 10:02:22 -05:00
|
|
|
assert_equal 2, x
|
2003-09-05 11:15:43 -04:00
|
|
|
|
2006-12-31 10:02:22 -05:00
|
|
|
Signal.trap(:INT) { raise "Interrupt" }
|
2008-09-24 13:44:39 -04:00
|
|
|
ex = assert_raise(RuntimeError) {
|
2006-12-31 10:02:22 -05:00
|
|
|
Process.kill :INT, Process.pid
|
2003-09-04 12:18:59 -04:00
|
|
|
sleep 0.1
|
2006-12-31 10:02:22 -05:00
|
|
|
}
|
|
|
|
assert_kind_of Exception, ex
|
|
|
|
assert_match(/Interrupt/, ex.message)
|
2003-10-19 22:31:47 -04:00
|
|
|
ensure
|
2006-12-31 10:02:22 -05:00
|
|
|
Signal.trap :INT, oldtrap if oldtrap
|
2003-09-04 12:18:59 -04:00
|
|
|
end
|
|
|
|
end
|
2005-06-03 10:39:15 -04:00
|
|
|
|
2011-05-15 09:37:47 -04:00
|
|
|
def test_signal_process_group
|
|
|
|
return unless Process.respond_to?(:kill)
|
2011-05-15 11:33:23 -04:00
|
|
|
return unless Process.respond_to?(:pgroup) # for mswin32
|
|
|
|
|
2011-05-15 09:37:47 -04:00
|
|
|
bug4362 = '[ruby-dev:43169]'
|
|
|
|
assert_nothing_raised(bug4362) do
|
|
|
|
pid = Process.spawn(EnvUtil.rubybin, '-e', '"sleep 10"', :pgroup => true)
|
|
|
|
Process.kill(:"-TERM", pid)
|
|
|
|
Process.waitpid(pid)
|
|
|
|
assert_equal(true, $?.signaled?)
|
|
|
|
assert_equal(Signal.list["TERM"], $?.termsig)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2005-06-03 10:39:15 -04:00
|
|
|
def test_exit_action
|
2010-04-27 08:27:13 -04:00
|
|
|
return unless have_fork? # skip this test
|
2005-06-03 10:39:15 -04:00
|
|
|
begin
|
|
|
|
r, w = IO.pipe
|
2005-07-03 05:37:49 -04:00
|
|
|
r0, w0 = IO.pipe
|
2010-08-09 09:12:54 -04:00
|
|
|
pid = Process.spawn(EnvUtil.rubybin, '-e', <<-'End', 3=>w, 4=>r0)
|
|
|
|
w = IO.new(3, "w")
|
|
|
|
r0 = IO.new(4, "r")
|
2006-12-31 10:02:22 -05:00
|
|
|
Signal.trap(:USR1, "EXIT")
|
2005-07-03 05:37:49 -04:00
|
|
|
w.syswrite("a")
|
2008-10-23 21:28:32 -04:00
|
|
|
Thread.start { sleep(2) }
|
2005-06-03 10:39:15 -04:00
|
|
|
r0.sysread(4096)
|
2010-08-09 09:12:54 -04:00
|
|
|
End
|
2005-07-03 05:37:49 -04:00
|
|
|
r.sysread(1)
|
2005-06-03 10:39:15 -04:00
|
|
|
sleep 0.1
|
|
|
|
assert_nothing_raised("[ruby-dev:26128]") {
|
|
|
|
Process.kill(:USR1, pid)
|
2005-06-05 03:04:06 -04:00
|
|
|
begin
|
2006-12-31 10:02:22 -05:00
|
|
|
Timeout.timeout(3) {
|
2005-06-05 03:04:06 -04:00
|
|
|
Process.waitpid pid
|
|
|
|
}
|
|
|
|
rescue Timeout::Error
|
|
|
|
Process.kill(:TERM, pid)
|
|
|
|
raise
|
|
|
|
end
|
2005-06-03 10:39:15 -04:00
|
|
|
}
|
|
|
|
ensure
|
|
|
|
r.close
|
|
|
|
w.close
|
2005-07-03 05:37:49 -04:00
|
|
|
r0.close
|
|
|
|
w0.close
|
2005-06-03 10:39:15 -04:00
|
|
|
end
|
|
|
|
end
|
2008-05-30 09:42:23 -04:00
|
|
|
|
|
|
|
def test_invalid_signal_name
|
|
|
|
return unless Process.respond_to?(:kill)
|
|
|
|
|
|
|
|
assert_raise(ArgumentError) { Process.kill(:XXXXXXXXXX, $$) }
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_signal_exception
|
|
|
|
assert_raise(ArgumentError) { SignalException.new }
|
|
|
|
assert_raise(ArgumentError) { SignalException.new(-1) }
|
|
|
|
assert_raise(ArgumentError) { SignalException.new(:XXXXXXXXXX) }
|
|
|
|
Signal.list.each do |signm, signo|
|
|
|
|
next if signm == "EXIT"
|
|
|
|
assert_equal(SignalException.new(signm).signo, signo)
|
|
|
|
assert_equal(SignalException.new(signm.to_sym).signo, signo)
|
|
|
|
assert_equal(SignalException.new(signo).signo, signo)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_interrupt
|
|
|
|
assert_raise(Interrupt) { raise Interrupt.new }
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_signal2
|
|
|
|
return unless Process.respond_to?(:kill)
|
|
|
|
begin
|
|
|
|
x = false
|
|
|
|
oldtrap = Signal.trap(:INT) {|sig| x = true }
|
|
|
|
GC.start
|
|
|
|
|
|
|
|
assert_raise(ArgumentError) { Process.kill }
|
|
|
|
|
|
|
|
Timeout.timeout(10) do
|
|
|
|
x = false
|
|
|
|
Process.kill(SignalException.new(:INT).signo, $$)
|
2011-05-27 09:37:37 -04:00
|
|
|
sleep(0.01) until x
|
2008-05-30 09:42:23 -04:00
|
|
|
|
|
|
|
x = false
|
|
|
|
Process.kill("INT", $$)
|
2011-05-27 09:37:37 -04:00
|
|
|
sleep(0.01) until x
|
2008-05-30 09:42:23 -04:00
|
|
|
|
|
|
|
x = false
|
|
|
|
Process.kill("SIGINT", $$)
|
2011-05-27 09:37:37 -04:00
|
|
|
sleep(0.01) until x
|
2008-05-30 09:42:23 -04:00
|
|
|
|
|
|
|
x = false
|
|
|
|
o = Object.new
|
|
|
|
def o.to_str; "SIGINT"; end
|
|
|
|
Process.kill(o, $$)
|
2011-05-27 09:37:37 -04:00
|
|
|
sleep(0.01) until x
|
2008-05-30 09:42:23 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
assert_raise(ArgumentError) { Process.kill(Object.new, $$) }
|
|
|
|
|
|
|
|
ensure
|
|
|
|
Signal.trap(:INT, oldtrap) if oldtrap
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_trap
|
|
|
|
return unless Process.respond_to?(:kill)
|
|
|
|
begin
|
|
|
|
oldtrap = Signal.trap(:INT) {|sig| }
|
|
|
|
|
|
|
|
assert_raise(ArgumentError) { Signal.trap }
|
|
|
|
|
|
|
|
assert_raise(SecurityError) do
|
|
|
|
s = proc {}.taint
|
|
|
|
Signal.trap(:INT, s)
|
|
|
|
end
|
|
|
|
|
|
|
|
# FIXME!
|
|
|
|
Signal.trap(:INT, nil)
|
|
|
|
Signal.trap(:INT, "")
|
|
|
|
Signal.trap(:INT, "SIG_IGN")
|
|
|
|
Signal.trap(:INT, "IGNORE")
|
|
|
|
|
|
|
|
Signal.trap(:INT, "SIG_DFL")
|
|
|
|
Signal.trap(:INT, "SYSTEM_DEFAULT")
|
|
|
|
|
|
|
|
Signal.trap(:INT, "EXIT")
|
|
|
|
|
2008-07-16 15:12:41 -04:00
|
|
|
Signal.trap(:INT, "xxxxxx")
|
|
|
|
Signal.trap(:INT, "xxxx")
|
2008-05-30 09:42:23 -04:00
|
|
|
|
|
|
|
Signal.trap(SignalException.new(:INT).signo, "SIG_DFL")
|
|
|
|
|
|
|
|
assert_raise(ArgumentError) { Signal.trap(-1, "xxxx") }
|
|
|
|
|
|
|
|
o = Object.new
|
|
|
|
def o.to_str; "SIGINT"; end
|
|
|
|
Signal.trap(o, "SIG_DFL")
|
|
|
|
|
|
|
|
assert_raise(ArgumentError) { Signal.trap("XXXXXXXXXX", "SIG_DFL") }
|
|
|
|
|
|
|
|
ensure
|
|
|
|
Signal.trap(:INT, oldtrap) if oldtrap
|
|
|
|
end
|
|
|
|
end
|
2010-04-27 08:27:13 -04:00
|
|
|
|
|
|
|
def test_kill_immediately_before_termination
|
|
|
|
return unless have_fork? # skip this test
|
|
|
|
|
|
|
|
r, w = IO.pipe
|
|
|
|
pid = Process.fork do
|
|
|
|
r.close
|
|
|
|
Signal.trap(:USR1) { w.syswrite("foo") }
|
|
|
|
Process.kill :USR1, $$
|
|
|
|
end
|
|
|
|
w.close
|
|
|
|
assert_equal(r.read, "foo")
|
|
|
|
end
|
2011-05-18 09:36:46 -04:00
|
|
|
|
|
|
|
def test_signal_requiring
|
|
|
|
t = Tempfile.new(%w"require_ensure_test .rb")
|
|
|
|
t.puts "sleep"
|
|
|
|
t.close
|
2011-06-01 12:32:15 -04:00
|
|
|
error = IO.popen([EnvUtil.rubybin, "-e", <<EOS, t.path, :err => File::NULL]) do |child|
|
2011-05-31 08:29:26 -04:00
|
|
|
trap(:INT, "DEFAULT")
|
2011-05-18 09:36:46 -04:00
|
|
|
th = Thread.new do
|
|
|
|
begin
|
|
|
|
require ARGV[0]
|
|
|
|
ensure
|
2012-05-03 18:50:30 -04:00
|
|
|
err = $! ? [$!, $!.backtrace] : $!
|
|
|
|
Marshal.dump(err, STDOUT)
|
2011-06-17 18:34:03 -04:00
|
|
|
STDOUT.flush
|
2011-05-18 09:36:46 -04:00
|
|
|
end
|
|
|
|
end
|
2012-05-11 11:24:27 -04:00
|
|
|
Thread.pass while th.running?
|
2011-06-17 18:34:03 -04:00
|
|
|
Process.kill(:INT, $$)
|
2011-05-18 09:36:46 -04:00
|
|
|
th.join
|
|
|
|
EOS
|
|
|
|
Marshal.load(child)
|
|
|
|
end
|
|
|
|
t.close!
|
|
|
|
assert_nil(error)
|
|
|
|
end
|
2011-07-12 00:55:50 -04:00
|
|
|
|
|
|
|
def test_reserved_signal
|
|
|
|
assert_raise(ArgumentError) {
|
|
|
|
Signal.trap(:SEGV) {}
|
|
|
|
}
|
|
|
|
assert_raise(ArgumentError) {
|
|
|
|
Signal.trap(:BUS) {}
|
|
|
|
}
|
|
|
|
assert_raise(ArgumentError) {
|
|
|
|
Signal.trap(:ILL) {}
|
|
|
|
}
|
|
|
|
assert_raise(ArgumentError) {
|
|
|
|
Signal.trap(:FPE) {}
|
|
|
|
}
|
|
|
|
assert_raise(ArgumentError) {
|
|
|
|
Signal.trap(:VTALRM) {}
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2012-11-19 04:43:53 -05:00
|
|
|
def test_signame
|
|
|
|
return unless Process.respond_to?(:kill)
|
|
|
|
|
2012-11-19 19:50:58 -05:00
|
|
|
10.times do
|
|
|
|
IO.popen([EnvUtil.rubybin, "-e", <<EOS, :err => File::NULL]) do |child|
|
|
|
|
Signal.trap("INT") do |signo|
|
|
|
|
signame = Signal.signame(signo)
|
|
|
|
Marshal.dump(signame, STDOUT)
|
|
|
|
STDOUT.flush
|
|
|
|
exit 0
|
|
|
|
end
|
2012-11-20 00:39:14 -05:00
|
|
|
Process.kill("INT", $$)
|
|
|
|
sleep 1 # wait signal deliver
|
2012-11-19 19:50:58 -05:00
|
|
|
EOS
|
2012-11-20 00:39:14 -05:00
|
|
|
|
2012-11-19 19:50:58 -05:00
|
|
|
signame = Marshal.load(child)
|
2012-11-19 04:43:53 -05:00
|
|
|
assert_equal(signame, "INT")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2012-11-28 03:30:51 -05:00
|
|
|
|
|
|
|
def test_trap_puts
|
|
|
|
assert_in_out_err([], <<-INPUT, ["a"*10000], [])
|
|
|
|
Signal.trap(:INT) {
|
|
|
|
# for enable internal io mutex
|
2013-01-31 02:08:23 -05:00
|
|
|
STDOUT.sync = false
|
2012-11-28 03:30:51 -05:00
|
|
|
# larger than internal io buffer
|
|
|
|
print "a"*10000
|
|
|
|
}
|
|
|
|
Process.kill :INT, $$
|
|
|
|
sleep 0.1
|
|
|
|
INPUT
|
|
|
|
end
|
2013-03-19 00:40:22 -04:00
|
|
|
|
|
|
|
def test_hup_me
|
|
|
|
# [Bug #7951] [ruby-core:52864]
|
2013-03-19 14:34:13 -04:00
|
|
|
# This is MRI specific spec. ruby has no guarantee
|
|
|
|
# that signal will be deliverd synchronously.
|
|
|
|
# This ugly workaround was introduced to don't break
|
|
|
|
# compatibility against silly example codes.
|
2013-03-19 00:40:22 -04:00
|
|
|
assert_raise(SignalException) {
|
|
|
|
Process.kill('HUP',Process.pid)
|
|
|
|
}
|
|
|
|
end
|
2003-09-04 12:18:59 -04:00
|
|
|
end
|