123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- <?php
- namespace React\Tests\EventLoop;
- use React\EventLoop\LoopInterface;
- use React\EventLoop\StreamSelectLoop;
- use React\EventLoop\Timer\Timer;
- class StreamSelectLoopTest extends AbstractLoopTest
- {
- protected function tearDown()
- {
- 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 signalProvider()
- {
- return [
- ['SIGUSR1'],
- ['SIGHUP'],
- ['SIGTERM'],
- ];
- }
- private $_signalHandled = false;
- /**
- * Test signal interrupt when no stream is attached to the loop
- * @dataProvider signalProvider
- */
- public function testSignalInterruptNoStream($signal)
- {
- if (!extension_loaded('pcntl')) {
- $this->markTestSkipped('"pcntl" extension is required to run this test.');
- }
- // dispatch signal handler once before signal is sent and once after
- $this->loop->addTimer(0.01, function() { pcntl_signal_dispatch(); });
- $this->loop->addTimer(0.03, function() { pcntl_signal_dispatch(); });
- if (defined('HHVM_VERSION')) {
- // hhvm startup is slow so we need to add another handler much later
- $this->loop->addTimer(0.5, function() { pcntl_signal_dispatch(); });
- }
- $this->setUpSignalHandler($signal);
- // spawn external process to send signal to current process id
- $this->forkSendSignal($signal);
- $this->loop->run();
- $this->assertTrue($this->_signalHandled);
- }
- /**
- * Test signal interrupt when a stream is attached to the loop
- * @dataProvider signalProvider
- */
- public function testSignalInterruptWithStream($signal)
- {
- if (!extension_loaded('pcntl')) {
- $this->markTestSkipped('"pcntl" extension is required to run this test.');
- }
- // dispatch signal handler every 10ms
- $this->loop->addPeriodicTimer(0.01, function() { pcntl_signal_dispatch(); });
- // add stream to the loop
- list($writeStream, $readStream) = $this->createSocketPair();
- $this->loop->addReadStream($readStream, function($stream, $loop) {
- /** @var $loop LoopInterface */
- $read = fgets($stream);
- if ($read === "end loop\n") {
- $loop->stop();
- }
- });
- $this->loop->addTimer(0.05, function() use ($writeStream) {
- fwrite($writeStream, "end loop\n");
- });
- $this->setUpSignalHandler($signal);
- // spawn external process to send signal to current process id
- $this->forkSendSignal($signal);
- $this->loop->run();
- $this->assertTrue($this->_signalHandled);
- }
- /**
- * add signal handler for signal
- */
- protected function setUpSignalHandler($signal)
- {
- $this->_signalHandled = false;
- $this->assertTrue(pcntl_signal(constant($signal), function() { $this->_signalHandled = true; }));
- }
- /**
- * 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();
- }
- }
- /**
- * https://github.com/reactphp/event-loop/issues/48
- *
- * Tests that timer with very small interval uses at least 1 microsecond
- * timeout.
- */
- public function testSmallTimerInterval()
- {
- /** @var StreamSelectLoop|\PHPUnit_Framework_MockObject_MockObject $loop */
- $loop = $this->getMock('React\EventLoop\StreamSelectLoop', ['streamSelect']);
- $loop
- ->expects($this->at(0))
- ->method('streamSelect')
- ->with([], [], 1);
- $loop
- ->expects($this->at(1))
- ->method('streamSelect')
- ->with([], [], 0);
- $callsCount = 0;
- $loop->addPeriodicTimer(Timer::MIN_INTERVAL, function() use (&$loop, &$callsCount) {
- $callsCount++;
- if ($callsCount == 2) {
- $loop->stop();
- }
- });
- $loop->run();
- }
- }
|