@@ -462,9 +462,210 @@ def test_itimer_prof(self):
462462 # and the handler should have been called
463463 self .assertEqual (self .hndl_called , True )
464464
465+
466+
467+ class SomeException (Exception ):
468+ """
469+ A unique exception class to be raised by a signal handler to verify that the
470+ signal handler was invoked.
471+ """
472+
473+
474+
475+ def raiser (* args ):
476+ """A signal handler which raises SomeException."""
477+ raise SomeException ()
478+
479+
480+
481+ class SigprocmaskTests (unittest .TestCase ):
482+ """Tests for sigprocmask."""
483+ def _handle_sigusr1 (self ):
484+ old_handler = signal .signal (signal .SIGUSR1 , raiser )
485+ self .addCleanup (signal .signal , signal .SIGUSR1 , old_handler )
486+
487+
488+ def test_signature (self ):
489+ """When invoked with other than two arguments, sigprocmask raises
490+ TypeError.
491+ """
492+ self .assertRaises (TypeError , signal .sigprocmask )
493+ self .assertRaises (TypeError , signal .sigprocmask , 1 )
494+ self .assertRaises (TypeError , signal .sigprocmask , 1 , 2 , 3 )
495+
496+
497+ def test_invalid_how (self ):
498+ """If a value other than SIG_BLOCK, SIG_UNBLOCK, or SIG_SETMASK is
499+ passed for the how argument to sigprocmask, ValueError is raised.
500+ """
501+ message = "value specified for how \(1700\) invalid"
502+ with self .assertRaisesRegexp (ValueError , message ):
503+ signal .sigprocmask (1700 , [])
504+
505+
506+ def test_invalid_signal_iterable (self ):
507+ """If iterating over the value passed for the signals parameter to
508+ sigprocmask raises an exception, sigprocmask raises that exception.
509+ """
510+ class BrokenIter (object ):
511+ def __iter__ (self ):
512+ raise RuntimeError ("my __iter__ is broken" )
513+ with self .assertRaisesRegexp (RuntimeError , "my __iter__ is broken" ):
514+ signal .sigprocmask (signal .SIG_BLOCK , BrokenIter ())
515+
516+
517+ def test_invalid_signal (self ):
518+ """If an object in the iterable passed for the signals parameter to
519+ sigprocmask isn't an integer, TypeError is raised."""
520+ with self .assertRaisesRegexp (TypeError , "an integer is required" ):
521+ signal .sigprocmask (signal .SIG_BLOCK , [object ()])
522+
523+
524+ def test_return_previous_mask (self ):
525+ """sigprocmask returns a list of the signals previously masked.
526+ """
527+ previous = signal .sigprocmask (signal .SIG_BLOCK , [1 , 3 , 5 ])
528+ result = signal .sigprocmask (signal .SIG_BLOCK , previous )
529+ self .assertEquals (result , [1 , 3 , 5 ])
530+
531+
532+ def test_block (self ):
533+ """When invoked with SIG_BLOCK, sigprocmask blocks the signals in the
534+ sigmask list.
535+ """
536+ self ._handle_sigusr1 ()
537+ previous = signal .sigprocmask (signal .SIG_BLOCK , [signal .SIGUSR1 ])
538+ os .kill (os .getpid (), signal .SIGUSR1 )
539+ with self .assertRaises (SomeException ):
540+ # Expect to receive SIGUSR1 after unblocking it.
541+ signal .sigprocmask (signal .SIG_SETMASK , previous )
542+
543+
544+ def test_unblock (self ):
545+ """When invoked with SIG_UNBLOCK, sigprocmask unblocks the signals in
546+ the sigmask list.
547+ """
548+ self ._handle_sigusr1 ()
549+ previous = signal .sigprocmask (signal .SIG_BLOCK , [signal .SIGUSR1 ])
550+ self .addCleanup (signal .sigprocmask , signal .SIG_SETMASK , previous )
551+ signal .sigprocmask (signal .SIG_UNBLOCK , [signal .SIGUSR1 ])
552+
553+ with self .assertRaises (SomeException ):
554+ os .kill (os .getpid (), signal .SIGUSR1 )
555+
556+
557+ def test_long_signals (self ):
558+ """sigprocmask accepts signal numbers as instances of long."""
559+ previous = signal .sigprocmask (
560+ signal .SIG_SETMASK , [long (signal .SIGUSR1 ), long (signal .SIGUSR2 )])
561+ masked = signal .sigprocmask (signal .SIG_SETMASK , previous )
562+ self .assertEquals (masked , [signal .SIGUSR1 , signal .SIGUSR2 ])
563+
564+
565+
566+ class SignalfdTests (unittest .TestCase ):
567+ """
568+ Tests for signal.signalfd.
569+ """
570+ def test_signature (self ):
571+ """When invoked with fewer than two arguments or more than three,
572+ signalfd raises TypeError.
573+ """
574+ self .assertRaises (TypeError , signal .signalfd )
575+ self .assertRaises (TypeError , signal .signalfd , 1 )
576+ self .assertRaises (TypeError , signal .signalfd , 1 , 2 , 3 , 4 )
577+
578+
579+ def test_create_signalfd (self ):
580+ """When invoked with a file descriptor of -1, signalfd allocates a new
581+ file descriptor for signal information delivery and returns it.
582+ """
583+ fd = signal .signalfd (- 1 , [])
584+ self .assertTrue (isinstance (fd , int ))
585+ os .close (fd )
586+
587+
588+ def test_non_iterable_signals (self ):
589+ """If an object which is not iterable is passed for the sigmask list
590+ argument to signalfd, the exception raised by trying to iterate over
591+ that object is raised.
592+ """
593+ self .assertRaises (TypeError , signal .signalfd , - 1 , object ())
594+
595+
596+ def test_non_integer_signals (self ):
597+ """If any non-integer values are included in the sigmask list argument
598+ to signalfd, the exception raised by the attempt to convert them to an
599+ integer is raised.
600+ """
601+ self .assertRaises (TypeError , signal .signalfd , - 1 , [object ()])
602+
603+
604+ def test_out_of_range_signal (self ):
605+ """If a signal number that is out of the valid range is included in the
606+ sigmask list argument to signalfd, ValueError is raised.
607+ """
608+ message = "signal number -2 out of range"
609+ with self .assertRaisesRegexp (ValueError , message ):
610+ signal .signalfd (- 1 , [- 2 ])
611+
612+
613+ def test_handle_signals (self ):
614+ """After signalfd is called, if a signal is received which was in the
615+ sigmask list passed to that call, information about the signal can be
616+ read from the fd returned by that call.
617+ """
618+ fd = signal .signalfd (- 1 , [signal .SIGUSR2 ])
619+ self .addCleanup (os .close , fd )
620+ previous = signal .sigprocmask (signal .SIG_BLOCK , [signal .SIGUSR2 ])
621+ self .addCleanup (signal .sigprocmask , signal .SIG_SETMASK , previous )
622+ os .kill (os .getpid (), signal .SIGUSR2 )
623+ bytes = os .read (fd , 128 )
624+ self .assertTrue (bytes )
625+
626+
627+ def test_close_on_exec (self ):
628+ """If the bit mask passed as the 3rd argument to signalfd includes
629+ SFD_CLOEXEC, the returned file descriptor has FD_CLOEXEC set on it.
630+ """
631+ import fcntl
632+ fd = signal .signalfd (- 1 , [], signal .SFD_CLOEXEC )
633+ self .addCleanup (os .close , fd )
634+ flags = fcntl .fcntl (fd , fcntl .F_GETFD )
635+ self .assertTrue (flags & fcntl .FD_CLOEXEC )
636+
637+
638+ def test_nonblocking (self ):
639+ """If the bit mask passed as the 3rd argument to signalfd includes
640+ SFD_NOBLOCK, the file description referenced by the returned file
641+ descriptor has O_NONBLOCK set on it.
642+ """
643+ import fcntl
644+ fd = signal .signalfd (- 1 , [], signal .SFD_NONBLOCK )
645+ self .addCleanup (os .close , fd )
646+ flags = fcntl .fcntl (fd , fcntl .F_GETFL )
647+ self .assertTrue (flags & os .O_NONBLOCK )
648+
649+
650+ def test_default_flags (self ):
651+ """If an empty bit mask is passed as the 3rd argument to signalfd,
652+ neither FD_CLOEXEC nor O_NONBLOCK is set on the resulting file
653+ descriptor/description.
654+ """
655+ import fcntl
656+ fd = signal .signalfd (- 1 , [])
657+ self .addCleanup (os .close , fd )
658+ flags = fcntl .fcntl (fd , fcntl .F_GETFD )
659+ self .assertFalse (flags & fcntl .FD_CLOEXEC )
660+ flags = fcntl .fcntl (fd , fcntl .F_GETFL )
661+ self .assertFalse (flags & os .O_NONBLOCK )
662+
663+
465664def test_main ():
466- support .run_unittest (BasicSignalTests , InterProcessSignalTests ,
467- WakeupSignalTests , SiginterruptTest , ItimerTest )
665+ support .run_unittest (
666+ BasicSignalTests , InterProcessSignalTests ,
667+ WakeupSignalTests , SiginterruptTest , ItimerTest , SignalfdTests ,
668+ SigprocmaskTests )
468669
469670
470671if __name__ == "__main__" :
0 commit comments