@@ -165,6 +165,53 @@ public function anIgnoredSignalStaysIgnored(): void
165165 Assert ::true ($ scope ->context ()->isCancelled ());
166166 }
167167
168+ public function aHandlerNotCallableFromTheScopeCountsAsTheDefaultDisposition (): void
169+ {
170+ $ name = 'privateHandler ' ;
171+ pcntl_signal (\SIGUSR1 , [$ this , $ name ]);
172+ $ this ->send (\SIGUSR1 );
173+ Assert ::same ($ this ->previous , [\SIGUSR1 ]);
174+
175+ $ scope = SignalScope ::listen (Context ::background (), [\SIGUSR1 ]);
176+ $ scope ->restore ();
177+
178+ Assert ::same (pcntl_signal_get_handler (\SIGUSR1 ), \SIG_DFL );
179+ $ this ->privateHandler (\SIGUSR2 );
180+ Assert ::same ($ this ->previous , [\SIGUSR1 , \SIGUSR2 ]);
181+ }
182+
183+ public function aSecondSignalWithTheDefaultDispositionIsReRaised (): void
184+ {
185+ $ scope = SignalScope ::listen (Context ::background (), [\SIGWINCH ]);
186+
187+ $ this ->send (\SIGWINCH );
188+ $ this ->send (\SIGWINCH );
189+
190+ Assert ::same (pcntl_signal_get_handler (\SIGWINCH ), \SIG_DFL );
191+ Assert ::same ($ scope ->signal (), \SIGWINCH );
192+
193+ // The kernel ignores SIGWINCH by default, so the re-raise above is
194+ // harmless but unobservable. Infection maps a mutant to the tests that
195+ // execute its line in this process, so the observable check (a forked
196+ // child that must die of the re-raised signal) lives in the same test.
197+ $ pid = pcntl_fork ();
198+ if ($ pid === 0 ) {
199+ try {
200+ pcntl_signal (\SIGUSR1 , \SIG_DFL );
201+ SignalScope ::listen (Context ::background (), [\SIGUSR1 ]);
202+ $ this ->send (\SIGUSR1 );
203+ $ this ->send (\SIGUSR1 );
204+ } finally {
205+ posix_kill (posix_getpid (), \SIGKILL );
206+ }
207+ }
208+
209+ pcntl_waitpid ($ pid , $ status );
210+
211+ Assert ::true (pcntl_wifsignaled ($ status ));
212+ Assert ::same (pcntl_wtermsig ($ status ), \SIGUSR1 );
213+ }
214+
168215 public function sighupCanBeHandled (): void
169216 {
170217 $ scope = SignalScope ::listen (Context ::background (), [\SIGHUP ]);
@@ -377,6 +424,12 @@ public function anInvalidSetLeavesNothingInstalled(): void
377424 }
378425 }
379426
427+ /** Registered by name: private, so not callable from SignalScope's scope. */
428+ private function privateHandler (int $ signal ): void
429+ {
430+ $ this ->previous [] = $ signal ;
431+ }
432+
380433 private function childCount (Context $ context ): int
381434 {
382435 $ state = (new \ReflectionProperty (Context::class, 'state ' ))->getValue ($ context );
0 commit comments