123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228 |
- <?php
- namespace React\Tests\EventLoop;
- use React\EventLoop\LoopInterface;
- use React\EventLoop\StreamSelectLoop;
- class StreamSelectLoopTest extends AbstractLoopTest
- {
- /**
- * @after
- */
- protected function tearDownSignalHandlers()
- {
- parent::tearDown();
- if (strncmp($this->getName(false), 'testSignal', 10) === 0 && extension_loaded('pcntl')) {
- $this->resetSignalHandlers();
- }
- }
- public function createLoop()
- {
- return new StreamSelectLoop();
- }
- public function testStreamSelectTimeoutEmulation()
- {
- $this->loop->addTimer(
- 0.05,
- $this->expectCallableOnce()
- );
- $start = microtime(true);
- $this->loop->run();
- $end = microtime(true);
- $interval = $end - $start;
- $this->assertGreaterThan(0.04, $interval);
- }
- public function testStreamSelectReportsWarningForStreamWithFilter()
- {
- if (defined('HHVM_VERSION')) {
- $this->markTestSkipped('Not supported on legacy HHVM');
- }
- $stream = tmpfile();
- stream_filter_append($stream, 'string.rot13');
- $this->loop->addReadStream($stream, $this->expectCallableNever());
- $loop = $this->loop;
- $this->loop->futureTick(function () use ($loop, $stream) {
- $loop->futureTick(function () use ($loop, $stream) {
- $loop->removeReadStream($stream);
- });
- });
- $error = null;
- $previous = set_error_handler(function ($_, $errstr) use (&$error) {
- $error = $errstr;
- });
- try {
- $this->loop->run();
- } catch (\ValueError $e) {
- // ignore ValueError for PHP 8+ due to empty stream array
- }
- restore_error_handler();
- $this->assertNotNull($error);
- $now = set_error_handler(function () { });
- restore_error_handler();
- $this->assertEquals($previous, $now);
- }
- public function testStreamSelectThrowsWhenCustomErrorHandlerThrowsForStreamWithFilter()
- {
- if (defined('HHVM_VERSION')) {
- $this->markTestSkipped('Not supported on legacy HHVM');
- }
- $stream = tmpfile();
- stream_filter_append($stream, 'string.rot13');
- $this->loop->addReadStream($stream, $this->expectCallableNever());
- $loop = $this->loop;
- $this->loop->futureTick(function () use ($loop, $stream) {
- $loop->futureTick(function () use ($loop, $stream) {
- $loop->removeReadStream($stream);
- });
- });
- $previous = set_error_handler(function ($_, $errstr) {
- throw new \RuntimeException($errstr);
- });
- $e = null;
- try {
- $this->loop->run();
- restore_error_handler();
- $this->fail();
- } catch (\RuntimeException $e) {
- restore_error_handler();
- } catch (\ValueError $e) {
- restore_error_handler(); // PHP 8+
- $e = $e->getPrevious();
- }
- $this->assertInstanceOf('RuntimeException', $e);
- $now = set_error_handler(function () { });
- restore_error_handler();
- $this->assertEquals($previous, $now);
- }
- public function signalProvider()
- {
- return array(
- array('SIGUSR1'),
- array('SIGHUP'),
- array('SIGTERM'),
- );
- }
- /**
- * Test signal interrupt when no stream is attached to the loop
- * @dataProvider signalProvider
- * @requires extension pcntl
- * @requires function pcntl_signal()
- * @requires function pcntl_signal_dispatch()
- */
- public function testSignalInterruptNoStream($signal)
- {
- // dispatch signal handler every 10ms for 0.1s
- $check = $this->loop->addPeriodicTimer(0.01, function() {
- pcntl_signal_dispatch();
- });
- $loop = $this->loop;
- $loop->addTimer(0.1, function () use ($check, $loop) {
- $loop->cancelTimer($check);
- });
- $handled = false;
- $this->assertTrue(pcntl_signal(constant($signal), function () use (&$handled) {
- $handled = true;
- }));
- // spawn external process to send signal to current process id
- $this->forkSendSignal($signal);
- $this->loop->run();
- $this->assertTrue($handled);
- }
- /**
- * Test signal interrupt when a stream is attached to the loop
- * @dataProvider signalProvider
- * @requires extension pcntl
- * @requires function pcntl_signal()
- * @requires function pcntl_signal_dispatch()
- */
- public function testSignalInterruptWithStream($signal)
- {
- // dispatch signal handler every 10ms
- $this->loop->addPeriodicTimer(0.01, function() {
- pcntl_signal_dispatch();
- });
- // add stream to the loop
- $loop = $this->loop;
- list($writeStream, $readStream) = $this->createSocketPair();
- $loop->addReadStream($readStream, function ($stream) use ($loop) {
- /** @var $loop LoopInterface */
- $read = fgets($stream);
- if ($read === "end loop\n") {
- $loop->stop();
- }
- });
- $this->loop->addTimer(0.1, function() use ($writeStream) {
- fwrite($writeStream, "end loop\n");
- });
- $handled = false;
- $this->assertTrue(pcntl_signal(constant($signal), function () use (&$handled) {
- $handled = true;
- }));
- // spawn external process to send signal to current process id
- $this->forkSendSignal($signal);
- $this->loop->run();
- $this->assertTrue($handled);
- }
- /**
- * reset all signal handlers to default
- */
- protected function resetSignalHandlers()
- {
- foreach($this->signalProvider() as $signal) {
- pcntl_signal(constant($signal[0]), SIG_DFL);
- }
- }
- /**
- * fork child process to send signal to current process id
- */
- protected function forkSendSignal($signal)
- {
- $currentPid = posix_getpid();
- $childPid = pcntl_fork();
- if ($childPid == -1) {
- $this->fail("Failed to fork child process!");
- } else if ($childPid === 0) {
- // this is executed in the child process
- usleep(20000);
- posix_kill($currentPid, constant($signal));
- die();
- }
- }
- }
|