2007-08-24 15:07:52 -03:00
|
|
|
import unittest
|
|
|
|
from test import test_support
|
2008-03-21 02:02:44 -03:00
|
|
|
from contextlib import closing, nested
|
|
|
|
import pickle
|
|
|
|
import select
|
1995-03-16 11:07:38 -04:00
|
|
|
import signal
|
2008-03-21 02:02:44 -03:00
|
|
|
import traceback
|
2008-02-28 17:00:45 -04:00
|
|
|
import sys, os, time, errno
|
|
|
|
|
|
|
|
if sys.platform[:3] in ('win', 'os2') or sys.platform == 'riscos':
|
|
|
|
raise test_support.TestSkipped("Can't test signal on %s" % \
|
|
|
|
sys.platform)
|
|
|
|
|
1997-04-15 21:29:15 -03:00
|
|
|
|
2004-08-07 18:27:43 -03:00
|
|
|
class HandlerBCalled(Exception):
|
|
|
|
pass
|
1995-03-16 11:07:38 -04:00
|
|
|
|
2008-03-21 02:02:44 -03:00
|
|
|
|
|
|
|
def exit_subprocess():
|
|
|
|
"""Use os._exit(0) to exit the current subprocess.
|
|
|
|
|
|
|
|
Otherwise, the test catches the SystemExit and continues executing
|
|
|
|
in parallel with the original test, so you wind up with an
|
|
|
|
exponential number of tests running concurrently.
|
|
|
|
"""
|
|
|
|
os._exit(0)
|
|
|
|
|
|
|
|
|
2007-08-24 15:07:52 -03:00
|
|
|
class InterProcessSignalTests(unittest.TestCase):
|
|
|
|
MAX_DURATION = 20 # Entire test should last at most 20 sec.
|
|
|
|
|
|
|
|
def handlerA(self, *args):
|
|
|
|
self.a_called = True
|
|
|
|
if test_support.verbose:
|
|
|
|
print "handlerA invoked", args
|
|
|
|
|
|
|
|
def handlerB(self, *args):
|
|
|
|
self.b_called = True
|
|
|
|
if test_support.verbose:
|
|
|
|
print "handlerB invoked", args
|
|
|
|
raise HandlerBCalled(*args)
|
|
|
|
|
2008-03-21 02:02:44 -03:00
|
|
|
def wait(self, child_pid):
|
|
|
|
"""Wait for child_pid to finish, ignoring EINTR."""
|
|
|
|
while True:
|
|
|
|
try:
|
2008-03-21 02:51:37 -03:00
|
|
|
os.waitpid(child_pid, 0)
|
|
|
|
return
|
2008-03-21 02:02:44 -03:00
|
|
|
except OSError as e:
|
2008-03-21 02:51:37 -03:00
|
|
|
if e.errno == errno.ECHILD:
|
|
|
|
return
|
2008-03-21 02:02:44 -03:00
|
|
|
if e.errno != errno.EINTR:
|
|
|
|
raise
|
2007-08-24 15:07:52 -03:00
|
|
|
|
2008-03-21 02:02:44 -03:00
|
|
|
def run_test(self):
|
|
|
|
# Install handlers. This function runs in a sub-process, so we
|
|
|
|
# don't worry about re-setting the default handlers.
|
|
|
|
signal.signal(signal.SIGHUP, self.handlerA)
|
|
|
|
signal.signal(signal.SIGUSR1, self.handlerB)
|
|
|
|
signal.signal(signal.SIGUSR2, signal.SIG_IGN)
|
|
|
|
signal.signal(signal.SIGALRM, signal.default_int_handler)
|
|
|
|
|
|
|
|
# Variables the signals will modify:
|
|
|
|
self.a_called = False
|
|
|
|
self.b_called = False
|
2007-08-24 15:07:52 -03:00
|
|
|
|
2008-03-21 02:02:44 -03:00
|
|
|
# Let the sub-processes know who to send signals to.
|
|
|
|
pid = os.getpid()
|
2007-08-24 15:07:52 -03:00
|
|
|
if test_support.verbose:
|
|
|
|
print "test runner's pid is", pid
|
|
|
|
|
2008-03-21 02:02:44 -03:00
|
|
|
child = os.fork()
|
|
|
|
if child == 0:
|
|
|
|
os.kill(pid, signal.SIGHUP)
|
|
|
|
exit_subprocess()
|
|
|
|
self.wait(child)
|
|
|
|
self.assertTrue(self.a_called)
|
|
|
|
self.assertFalse(self.b_called)
|
|
|
|
self.a_called = False
|
2007-08-24 15:07:52 -03:00
|
|
|
|
|
|
|
try:
|
2008-03-21 02:02:44 -03:00
|
|
|
child = os.fork()
|
|
|
|
if child == 0:
|
|
|
|
os.kill(pid, signal.SIGUSR1)
|
|
|
|
exit_subprocess()
|
|
|
|
# This wait should be interrupted by the signal's exception.
|
|
|
|
self.wait(child)
|
|
|
|
self.fail('HandlerBCalled exception not thrown')
|
|
|
|
except HandlerBCalled:
|
|
|
|
# So we call it again to reap the child's zombie.
|
|
|
|
self.wait(child)
|
|
|
|
self.assertTrue(self.b_called)
|
|
|
|
self.assertFalse(self.a_called)
|
2007-08-24 15:07:52 -03:00
|
|
|
if test_support.verbose:
|
2008-03-21 02:02:44 -03:00
|
|
|
print "HandlerBCalled exception caught"
|
2007-08-24 15:07:52 -03:00
|
|
|
|
2008-03-21 02:02:44 -03:00
|
|
|
child = os.fork()
|
|
|
|
if child == 0:
|
|
|
|
os.kill(pid, signal.SIGUSR2)
|
|
|
|
exit_subprocess()
|
|
|
|
self.wait(child) # Nothing should happen.
|
|
|
|
|
|
|
|
try:
|
|
|
|
signal.alarm(1)
|
|
|
|
# The race condition in pause doesn't matter in this case,
|
|
|
|
# since alarm is going to raise a KeyboardException, which
|
|
|
|
# will skip the call.
|
|
|
|
signal.pause()
|
2007-08-24 15:07:52 -03:00
|
|
|
except KeyboardInterrupt:
|
|
|
|
if test_support.verbose:
|
|
|
|
print "KeyboardInterrupt (the alarm() went off)"
|
|
|
|
except:
|
2008-03-21 02:02:44 -03:00
|
|
|
self.fail('Some other exception woke us from pause: %s' %
|
|
|
|
traceback.format_exc())
|
|
|
|
else:
|
|
|
|
self.fail('pause returned of its own accord')
|
|
|
|
|
|
|
|
def test_main(self):
|
|
|
|
# This function spawns a child process to insulate the main
|
|
|
|
# test-running process from all the signals. It then
|
|
|
|
# communicates with that child process over a pipe and
|
|
|
|
# re-raises information about any exceptions the child
|
|
|
|
# throws. The real work happens in self.run_test().
|
|
|
|
os_done_r, os_done_w = os.pipe()
|
|
|
|
with nested(closing(os.fdopen(os_done_r)),
|
|
|
|
closing(os.fdopen(os_done_w, 'w'))) as (done_r, done_w):
|
|
|
|
child = os.fork()
|
|
|
|
if child == 0:
|
|
|
|
# In the child process; run the test and report results
|
|
|
|
# through the pipe.
|
|
|
|
try:
|
|
|
|
done_r.close()
|
|
|
|
# Have to close done_w again here because
|
|
|
|
# exit_subprocess() will skip the enclosing with block.
|
|
|
|
with closing(done_w):
|
|
|
|
try:
|
|
|
|
self.run_test()
|
|
|
|
except:
|
|
|
|
pickle.dump(traceback.format_exc(), done_w)
|
|
|
|
else:
|
|
|
|
pickle.dump(None, done_w)
|
|
|
|
except:
|
|
|
|
print 'Uh oh, raised from pickle.'
|
|
|
|
traceback.print_exc()
|
|
|
|
finally:
|
|
|
|
exit_subprocess()
|
|
|
|
|
|
|
|
done_w.close()
|
|
|
|
# Block for up to MAX_DURATION seconds for the test to finish.
|
|
|
|
r, w, x = select.select([done_r], [], [], self.MAX_DURATION)
|
|
|
|
if done_r in r:
|
|
|
|
tb = pickle.load(done_r)
|
|
|
|
if tb:
|
|
|
|
self.fail(tb)
|
|
|
|
else:
|
|
|
|
os.kill(child, signal.SIGKILL)
|
|
|
|
self.fail('Test deadlocked after %d seconds.' %
|
|
|
|
self.MAX_DURATION)
|
2007-08-24 15:07:52 -03:00
|
|
|
|
|
|
|
|
|
|
|
class BasicSignalTests(unittest.TestCase):
|
2008-03-21 02:02:44 -03:00
|
|
|
def trivial_signal_handler(self, *args):
|
|
|
|
pass
|
|
|
|
|
2007-08-24 15:07:52 -03:00
|
|
|
def test_out_of_range_signal_number_raises_error(self):
|
|
|
|
self.assertRaises(ValueError, signal.getsignal, 4242)
|
|
|
|
|
|
|
|
self.assertRaises(ValueError, signal.signal, 4242,
|
2008-03-21 02:02:44 -03:00
|
|
|
self.trivial_signal_handler)
|
2007-08-24 15:07:52 -03:00
|
|
|
|
|
|
|
def test_setting_signal_handler_to_none_raises_error(self):
|
|
|
|
self.assertRaises(TypeError, signal.signal,
|
|
|
|
signal.SIGUSR1, None)
|
|
|
|
|
2008-03-21 02:02:44 -03:00
|
|
|
def test_getsignal(self):
|
|
|
|
hup = signal.signal(signal.SIGHUP, self.trivial_signal_handler)
|
|
|
|
self.assertEquals(signal.getsignal(signal.SIGHUP),
|
|
|
|
self.trivial_signal_handler)
|
|
|
|
signal.signal(signal.SIGHUP, hup)
|
|
|
|
self.assertEquals(signal.getsignal(signal.SIGHUP), hup)
|
|
|
|
|
|
|
|
|
2007-12-19 15:41:06 -04:00
|
|
|
class WakeupSignalTests(unittest.TestCase):
|
|
|
|
TIMEOUT_FULL = 10
|
|
|
|
TIMEOUT_HALF = 5
|
|
|
|
|
|
|
|
def test_wakeup_fd_early(self):
|
|
|
|
import select
|
|
|
|
|
|
|
|
signal.alarm(1)
|
|
|
|
before_time = time.time()
|
|
|
|
# We attempt to get a signal during the sleep,
|
|
|
|
# before select is called
|
|
|
|
time.sleep(self.TIMEOUT_FULL)
|
|
|
|
mid_time = time.time()
|
|
|
|
self.assert_(mid_time - before_time < self.TIMEOUT_HALF)
|
|
|
|
select.select([self.read], [], [], self.TIMEOUT_FULL)
|
|
|
|
after_time = time.time()
|
|
|
|
self.assert_(after_time - mid_time < self.TIMEOUT_HALF)
|
|
|
|
|
|
|
|
def test_wakeup_fd_during(self):
|
|
|
|
import select
|
|
|
|
|
|
|
|
signal.alarm(1)
|
|
|
|
before_time = time.time()
|
|
|
|
# We attempt to get a signal during the select call
|
|
|
|
self.assertRaises(select.error, select.select,
|
|
|
|
[self.read], [], [], self.TIMEOUT_FULL)
|
|
|
|
after_time = time.time()
|
|
|
|
self.assert_(after_time - before_time < self.TIMEOUT_HALF)
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
import fcntl
|
|
|
|
|
|
|
|
self.alrm = signal.signal(signal.SIGALRM, lambda x,y:None)
|
|
|
|
self.read, self.write = os.pipe()
|
|
|
|
flags = fcntl.fcntl(self.write, fcntl.F_GETFL, 0)
|
|
|
|
flags = flags | os.O_NONBLOCK
|
|
|
|
fcntl.fcntl(self.write, fcntl.F_SETFL, flags)
|
|
|
|
self.old_wakeup = signal.set_wakeup_fd(self.write)
|
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
signal.set_wakeup_fd(self.old_wakeup)
|
|
|
|
os.close(self.read)
|
|
|
|
os.close(self.write)
|
|
|
|
signal.signal(signal.SIGALRM, self.alrm)
|
|
|
|
|
2008-02-23 11:07:35 -04:00
|
|
|
class SiginterruptTest(unittest.TestCase):
|
|
|
|
signum = signal.SIGUSR1
|
|
|
|
def readpipe_interrupted(self, cb):
|
|
|
|
r, w = os.pipe()
|
|
|
|
ppid = os.getpid()
|
|
|
|
pid = os.fork()
|
|
|
|
|
|
|
|
oldhandler = signal.signal(self.signum, lambda x,y: None)
|
|
|
|
cb()
|
|
|
|
if pid==0:
|
|
|
|
# child code: sleep, kill, sleep. and then exit,
|
|
|
|
# which closes the pipe from which the parent process reads
|
|
|
|
try:
|
|
|
|
time.sleep(0.2)
|
|
|
|
os.kill(ppid, self.signum)
|
|
|
|
time.sleep(0.2)
|
|
|
|
finally:
|
2008-03-21 02:02:44 -03:00
|
|
|
exit_subprocess()
|
2008-02-23 11:07:35 -04:00
|
|
|
|
|
|
|
try:
|
|
|
|
os.close(w)
|
|
|
|
|
|
|
|
try:
|
|
|
|
d=os.read(r, 1)
|
|
|
|
return False
|
|
|
|
except OSError, err:
|
|
|
|
if err.errno != errno.EINTR:
|
|
|
|
raise
|
|
|
|
return True
|
|
|
|
finally:
|
|
|
|
signal.signal(self.signum, oldhandler)
|
|
|
|
os.waitpid(pid, 0)
|
|
|
|
|
|
|
|
def test_without_siginterrupt(self):
|
|
|
|
i=self.readpipe_interrupted(lambda: None)
|
|
|
|
self.assertEquals(i, True)
|
|
|
|
|
|
|
|
def test_siginterrupt_on(self):
|
|
|
|
i=self.readpipe_interrupted(lambda: signal.siginterrupt(self.signum, 1))
|
|
|
|
self.assertEquals(i, True)
|
|
|
|
|
|
|
|
def test_siginterrupt_off(self):
|
|
|
|
i=self.readpipe_interrupted(lambda: signal.siginterrupt(self.signum, 0))
|
|
|
|
self.assertEquals(i, False)
|
2007-12-19 15:41:06 -04:00
|
|
|
|
2007-08-24 15:07:52 -03:00
|
|
|
def test_main():
|
2007-12-19 15:41:06 -04:00
|
|
|
test_support.run_unittest(BasicSignalTests, InterProcessSignalTests,
|
2008-02-23 11:07:35 -04:00
|
|
|
WakeupSignalTests, SiginterruptTest)
|
2007-08-24 15:07:52 -03:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
test_main()
|