SignalHandlerTest.php 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. <?php
  2. namespace Seld\Signal;
  3. use PHPUnit\Framework\TestCase;
  4. use Psr\Log\LoggerInterface;
  5. class SignalHandlerTest extends TestCase
  6. {
  7. /**
  8. * @requires extension pcntl
  9. * @requires extension posix
  10. */
  11. public function testLoggingAndDefault(): void
  12. {
  13. $log = $this->getMockBuilder(LoggerInterface::class)->getMock();
  14. $signal = SignalHandler::create(null, $log);
  15. $log->expects(self::exactly(2))
  16. ->method('info')
  17. ->withConsecutive(
  18. ['Received SIGINT', []],
  19. ['Received SIGTERM', []]
  20. );
  21. posix_kill(posix_getpid(), SIGINT);
  22. posix_kill(posix_getpid(), SIGTERM);
  23. posix_kill(posix_getpid(), SIGURG);
  24. }
  25. /**
  26. * @requires extension pcntl
  27. * @requires extension posix
  28. * @requires PHP < 8.0
  29. */
  30. public function testNoAutoGCOnPHP7(): void
  31. {
  32. $log = $this->getMockBuilder(LoggerInterface::class)->getMock();
  33. $signal = SignalHandler::create(null, $log);
  34. $log->expects(self::exactly(2))
  35. ->method('info')
  36. ->withConsecutive(
  37. ['Received SIGINT', []],
  38. ['Received SIGINT', []]
  39. );
  40. posix_kill(posix_getpid(), SIGINT);
  41. unset($signal);
  42. posix_kill(posix_getpid(), SIGINT);
  43. SignalHandler::unregisterAll();
  44. self::assertSame(SIG_DFL, pcntl_signal_get_handler(SIGINT));
  45. }
  46. /**
  47. * @requires extension pcntl
  48. * @requires extension posix
  49. * @requires PHP >= 8.0
  50. */
  51. public function testAutoGCOnPHP8(): void
  52. {
  53. $log1 = $this->getMockBuilder(LoggerInterface::class)->getMock();
  54. $signal1 = SignalHandler::create(null, $log1);
  55. $log1->expects(self::exactly(1))
  56. ->method('info')
  57. ->withConsecutive(
  58. ['Received SIGINT', []]
  59. );
  60. $log2 = $this->getMockBuilder(LoggerInterface::class)->getMock();
  61. $signal2 = SignalHandler::create(null, $log2);
  62. $log2->expects(self::exactly(1))
  63. ->method('info')
  64. ->withConsecutive(
  65. ['Received SIGINT', []]
  66. );
  67. posix_kill(posix_getpid(), SIGINT);
  68. unset($signal2);
  69. posix_kill(posix_getpid(), SIGINT);
  70. unset($signal1);
  71. self::assertSame(SIG_DFL, pcntl_signal_get_handler(SIGINT));
  72. }
  73. /**
  74. * @requires extension pcntl
  75. * @requires extension posix
  76. */
  77. public function testCallbackAndCustom(): void
  78. {
  79. pcntl_signal(SIGINT, function () {
  80. // ignore
  81. });
  82. $sigName = null;
  83. $signal = SignalHandler::create([SignalHandler::SIGHUP], function ($name) use (&$sigName) {
  84. $sigName = $name;
  85. });
  86. posix_kill(posix_getpid(), SIGINT);
  87. self::assertNull($sigName);
  88. posix_kill(posix_getpid(), SIGHUP);
  89. self::assertSame('SIGHUP', $sigName);
  90. $signal->unregister();
  91. }
  92. /**
  93. * @requires extension pcntl
  94. * @requires extension posix
  95. */
  96. public function testTriggerResetCycle(): void
  97. {
  98. $signal = SignalHandler::create([SignalHandler::SIGUSR1, SignalHandler::SIGUSR2]);
  99. self::assertFalse($signal->isTriggered());
  100. posix_kill(posix_getpid(), SIGUSR1);
  101. self::assertTrue($signal->isTriggered());
  102. $signal->reset();
  103. self::assertFalse($signal->isTriggered());
  104. posix_kill(posix_getpid(), SIGUSR2);
  105. self::assertTrue($signal->isTriggered());
  106. $signal->unregister();
  107. }
  108. /**
  109. * @requires extension pcntl
  110. * @requires extension posix
  111. */
  112. public function testNestingWorks(): void
  113. {
  114. $log1 = $this->getMockBuilder(LoggerInterface::class)->getMock();
  115. $signal1 = SignalHandler::create([SignalHandler::SIGINT, SignalHandler::SIGHUP], $log1);
  116. $log1->expects(self::exactly(2))
  117. ->method('info')
  118. ->withConsecutive(
  119. ['Received SIGHUP', []],
  120. ['Received SIGINT', []]
  121. );
  122. $log2 = $this->getMockBuilder(LoggerInterface::class)->getMock();
  123. $signal2 = SignalHandler::create([SignalHandler::SIGINT], $log2);
  124. $log2->expects(self::exactly(1))
  125. ->method('info')
  126. ->withConsecutive(
  127. ['Received SIGINT', []]
  128. );
  129. posix_kill(posix_getpid(), SIGINT);
  130. posix_kill(posix_getpid(), SIGHUP);
  131. $signal2->unregister();
  132. unset($signal2);
  133. self::assertNotSame(SIG_DFL, pcntl_signal_get_handler(SIGINT));
  134. self::assertNotSame(SIG_DFL, pcntl_signal_get_handler(SIGHUP));
  135. posix_kill(posix_getpid(), SIGINT);
  136. $signal1->unregister();
  137. unset($signal1);
  138. self::assertSame(SIG_DFL, pcntl_signal_get_handler(SIGINT));
  139. self::assertSame(SIG_DFL, pcntl_signal_get_handler(SIGHUP));
  140. }
  141. /**
  142. * @requires OSFAMILY Windows
  143. * @requires PHP >= 7.4
  144. */
  145. public function testLoggingAndDefaultOnWindows(): void
  146. {
  147. $log = $this->getMockBuilder(LoggerInterface::class)->getMock();
  148. $signal = SignalHandler::create([SignalHandler::SIGINT, SignalHandler::SIGBREAK], $log);
  149. $log->expects(self::atLeastOnce())
  150. ->method('info')
  151. ->with(self::equalTo('Received SIGBREAK'));
  152. $this->dispatchWindowsSignal($signal, PHP_WINDOWS_EVENT_CTRL_BREAK);
  153. $signal->unregister();
  154. }
  155. /**
  156. * @requires OSFAMILY Windows
  157. * @requires PHP >= 7.4
  158. */
  159. public function testCallbackAndCustomOnWindows(): void
  160. {
  161. $sigName = null;
  162. $signal = SignalHandler::create([SignalHandler::SIGBREAK], function ($name) use (&$sigName) {
  163. $sigName = $name;
  164. });
  165. $this->dispatchWindowsSignal($signal, PHP_WINDOWS_EVENT_CTRL_BREAK);
  166. self::assertSame(SignalHandler::SIGBREAK, $sigName);
  167. $signal->unregister();
  168. }
  169. /**
  170. * @requires OSFAMILY Windows
  171. * @requires PHP >= 7.4
  172. */
  173. public function testTriggerResetCycleOnWindows(): void
  174. {
  175. $signal = SignalHandler::create([SignalHandler::SIGINT, SignalHandler::SIGBREAK]);
  176. self::assertFalse($signal->isTriggered());
  177. $this->dispatchWindowsSignal($signal, PHP_WINDOWS_EVENT_CTRL_BREAK);
  178. self::assertTrue($signal->isTriggered());
  179. $signal->reset();
  180. self::assertFalse($signal->isTriggered());
  181. $this->dispatchWindowsSignal($signal, PHP_WINDOWS_EVENT_CTRL_BREAK);
  182. self::assertTrue($signal->isTriggered());
  183. $signal->unregister();
  184. }
  185. /**
  186. * @requires OSFAMILY Windows
  187. * @requires PHP >= 7.4
  188. */
  189. public function testNestingWorksOnWindows(): void
  190. {
  191. $signal1 = SignalHandler::create([SignalHandler::SIGINT, SignalHandler::SIGBREAK]);
  192. $signal2 = SignalHandler::create([SignalHandler::SIGINT, SignalHandler::SIGBREAK]);
  193. self::assertFalse($signal1->isTriggered());
  194. self::assertFalse($signal2->isTriggered());
  195. $this->dispatchWindowsSignal($signal2, PHP_WINDOWS_EVENT_CTRL_BREAK);
  196. self::assertFalse($signal1->isTriggered());
  197. self::assertTrue($signal2->isTriggered());
  198. $signal2->unregister();
  199. unset($signal2);
  200. $this->dispatchWindowsSignal($signal1, PHP_WINDOWS_EVENT_CTRL_BREAK);
  201. self::assertTrue($signal1->isTriggered());
  202. $signal1->unregister();
  203. unset($signal1);
  204. }
  205. private function dispatchWindowsSignal(SignalHandler $handler, int $signal): void
  206. {
  207. sapi_windows_generate_ctrl_event($signal);
  208. sapi_windows_generate_ctrl_event($signal);
  209. sapi_windows_generate_ctrl_event($signal);
  210. // waits to try and get the signal handler to trigger
  211. $tries = 10;
  212. while (!$handler->isTriggered() && $tries-- > 0) {
  213. sapi_windows_generate_ctrl_event($signal);
  214. usleep(500000);
  215. }
  216. }
  217. }