2016-05-24 10:37:25 +02:00
< ? php
2017-03-12 11:21:44 +01:00
namespace Amp\Test\Loop ;
2016-05-24 10:37:25 +02:00
2017-03-15 00:34:37 +01:00
use Amp\Coroutine ;
2017-09-17 12:18:04 +02:00
use Amp\Delayed ;
2017-03-15 00:34:37 +01:00
use Amp\Failure ;
2017-03-12 22:09:19 +01:00
use Amp\Loop ;
2017-03-10 21:31:57 +01:00
use Amp\Loop\Driver ;
2017-03-14 17:50:24 +01:00
use Amp\Loop\InvalidWatcherError ;
2017-03-10 21:31:57 +01:00
use Amp\Loop\UnsupportedFeatureException ;
2017-03-12 11:21:44 +01:00
use PHPUnit\Framework\TestCase ;
2017-03-12 21:02:26 +01:00
use React\Promise\RejectedPromise as RejectedReactPromise ;
2020-07-14 21:45:35 +02:00
use function Amp\getCurrentTime ;
2016-05-24 10:37:25 +02:00
2017-11-29 13:36:50 +01:00
if ( ! \defined ( " SIGUSR1 " )) {
\define ( " SIGUSR1 " , 30 );
2016-08-14 21:14:43 +02:00
}
2017-11-29 13:36:50 +01:00
if ( ! \defined ( " SIGUSR2 " )) {
\define ( " SIGUSR2 " , 31 );
2016-05-27 03:25:55 +02:00
}
2017-11-29 13:36:50 +01:00
if ( ! \defined ( " PHP_INT_MIN " )) {
\define ( " PHP_INT_MIN " , ~ PHP_INT_MAX );
2016-08-14 22:44:49 +02:00
}
2018-06-18 20:00:01 +02:00
abstract class DriverTest extends TestCase
{
2016-06-03 17:01:21 +02:00
/**
2017-04-24 15:39:08 +02:00
* The DriverFactory to run this test on .
2016-06-03 17:01:21 +02:00
*
2017-03-10 21:31:57 +01:00
* @ return callable
2016-06-03 17:01:21 +02:00
*/
2017-04-24 15:39:08 +02:00
abstract public function getFactory () : callable ;
2016-06-03 17:01:21 +02:00
/** @var Driver */
public $loop ;
2018-06-18 20:00:01 +02:00
public function setUp ()
{
2017-03-10 21:31:57 +01:00
$this -> loop = ( $this -> getFactory ())();
2017-03-12 22:09:19 +01:00
2016-06-03 17:01:21 +02:00
if ( ! $this -> loop instanceof Driver ) {
$this -> fail ( " Factory did not return a loop Driver " );
}
2017-03-12 22:09:19 +01:00
// Required for error handler to work
Loop :: set ( $this -> loop );
2017-11-29 13:36:50 +01:00
\gc_collect_cycles ();
2017-05-07 13:09:59 +02:00
}
2018-06-18 20:00:01 +02:00
public function tearDown ()
{
2017-05-07 13:09:59 +02:00
unset ( $this -> loop );
2016-06-03 17:01:21 +02:00
}
2018-06-18 20:00:01 +02:00
public function start ( $cb )
{
2016-06-03 17:01:21 +02:00
$cb ( $this -> loop );
$this -> loop -> run ();
}
2018-06-18 20:00:01 +02:00
public function testEmptyLoop ()
{
2017-03-14 00:52:57 +01:00
$this -> assertNull ( $this -> loop -> run ());
2016-06-03 17:01:21 +02:00
}
2018-06-18 20:00:01 +02:00
public function testStopWorksEvenIfNotCurrentlyRunning ()
{
2017-03-14 00:52:57 +01:00
$this -> assertNull ( $this -> loop -> stop ());
2016-06-03 17:01:21 +02:00
}
// Note: The running nesting is important for being able to continue actually still running loops (i.e. running flag set, if the driver has one) inside register_shutdown_function() for example
2018-06-18 20:00:01 +02:00
public function testLoopRunsCanBeConsecutiveAndNested ()
{
2016-06-03 17:01:21 +02:00
$this -> expectOutputString ( " 123456 " );
$this -> start ( function ( Driver $loop ) {
2016-06-07 01:53:28 +02:00
$loop -> stop ();
2017-03-10 21:31:57 +01:00
$loop -> defer ( function () use ( & $run ) {
2016-06-07 01:53:28 +02:00
echo $run = 1 ;
2016-06-03 17:01:21 +02:00
});
$loop -> run ();
2016-06-07 01:53:28 +02:00
if ( ! $run ) {
$this -> fail ( " A loop stop before a run must not impact that run " );
}
2017-03-10 21:31:57 +01:00
$loop -> defer ( function () use ( $loop ) {
2016-06-03 17:01:21 +02:00
$loop -> run ();
2017-03-27 07:05:55 +02:00
echo 5 ;
2017-03-10 21:31:57 +01:00
$loop -> defer ( function () use ( $loop ) {
2016-06-03 17:01:21 +02:00
echo 6 ;
$loop -> stop ();
2017-03-10 21:31:57 +01:00
$loop -> defer ( function () {
2016-06-07 01:53:28 +02:00
$this -> fail ( " A loop stopped at all levels must not execute further defers " );
2016-06-03 17:01:21 +02:00
});
});
2017-03-27 07:05:55 +02:00
$loop -> run ();
2016-06-03 17:01:21 +02:00
});
2017-03-10 21:31:57 +01:00
$loop -> defer ( function () use ( $loop ) {
2016-06-03 17:01:21 +02:00
echo 2 ;
2017-12-05 08:48:36 +01:00
$loop -> defer ( function () {
2017-03-27 07:05:55 +02:00
echo 4 ;
2016-06-03 17:01:21 +02:00
});
});
2017-12-05 08:48:36 +01:00
$loop -> defer ( function () {
2016-06-03 17:01:21 +02:00
echo 3 ;
});
});
}
2020-07-14 21:45:35 +02:00
public function testCorrectTimeoutIfBlockingBeforeActivate ()
{
$start = 0 ;
$invoked = 0 ;
$this -> start ( function ( Driver $loop ) use ( & $start , & $invoked ) {
$loop -> defer ( function () use ( $loop , & $start , & $invoked ) {
$start = getCurrentTime ();
$loop -> delay ( 1000 , function () use ( & $invoked ) {
$invoked = getCurrentTime ();
});
\usleep ( 500000 );
});
});
$this -> assertNotSame ( 0 , $start );
$this -> assertNotSame ( 0 , $invoked );
$this -> assertGreaterThanOrEqual ( 999 , $invoked - $start );
$this -> assertLessThan ( 1100 , $invoked - $start );
}
public function testCorrectTimeoutIfBlockingBeforeDelay ()
{
$start = 0 ;
$invoked = 0 ;
$this -> start ( function ( Driver $loop ) use ( & $start , & $invoked ) {
$start = getCurrentTime ();
\usleep ( 500000 );
$loop -> delay ( 1000 , function () use ( & $invoked ) {
$invoked = getCurrentTime ();
});
});
$this -> assertNotSame ( 0 , $start );
$this -> assertNotSame ( 0 , $invoked );
$this -> assertGreaterThanOrEqual ( 1500 , $invoked - $start );
$this -> assertLessThan ( 1600 , $invoked - $start );
}
2018-06-18 20:00:01 +02:00
public function testLoopTerminatesWithOnlyUnreferencedWatchers ()
{
2017-07-09 09:28:32 +02:00
$this -> start ( function ( Driver $loop ) use ( & $end ) {
2018-06-18 20:00:01 +02:00
$loop -> unreference ( $loop -> onReadable ( STDIN , function () {
}));
$w = $loop -> delay ( 10000000 , function () {
});
$loop -> defer ( function () use ( $loop , $w ) {
$loop -> cancel ( $w );
});
2017-07-09 09:28:32 +02:00
$end = true ;
});
$this -> assertTrue ( $end );
}
2017-03-12 17:33:46 +01:00
/** This MUST NOT have a "test" prefix, otherwise it's executed as test and marked as risky. */
2018-06-18 20:00:01 +02:00
public function checkForSignalCapability ()
{
2016-06-03 17:01:21 +02:00
try {
2017-03-15 00:34:37 +01:00
$watcher = $this -> loop -> onSignal ( SIGUSR1 , function () {
});
2016-06-03 17:01:21 +02:00
$this -> loop -> cancel ( $watcher );
} catch ( UnsupportedFeatureException $e ) {
$this -> markTestSkipped ( " The loop is not capable of handling signals properly. Skipping. " );
}
}
2018-06-18 20:00:01 +02:00
public function testWatcherUnrefRerefRunResult ()
{
2016-06-03 17:01:21 +02:00
$invoked = false ;
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) use ( & $invoked ) {
$watcher = $loop -> defer ( function () use ( & $invoked ) {
2016-06-03 17:01:21 +02:00
$invoked = true ;
});
$loop -> unreference ( $watcher );
2016-06-07 01:53:28 +02:00
$loop -> unreference ( $watcher );
2016-06-03 17:01:21 +02:00
$loop -> reference ( $watcher );
});
$this -> assertTrue ( $invoked );
}
2018-06-18 20:00:01 +02:00
public function testDeferWatcherUnrefRunResult ()
{
2017-03-14 00:52:57 +01:00
$invoked = false ;
$this -> start ( function ( Driver $loop ) use ( & $invoked ) {
$watcher = $loop -> defer ( function () use ( & $invoked ) {
$invoked = true ;
2016-06-03 17:01:21 +02:00
});
$loop -> unreference ( $watcher );
});
2017-03-14 00:52:57 +01:00
$this -> assertFalse ( $invoked );
2016-06-03 17:01:21 +02:00
}
2018-06-18 20:00:01 +02:00
public function testOnceWatcherUnrefRunResult ()
{
2016-06-03 17:01:21 +02:00
$invoked = false ;
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) use ( & $invoked ) {
$watcher = $loop -> delay ( 2000 , function () use ( & $invoked ) {
2016-06-03 17:01:21 +02:00
$invoked = true ;
});
$loop -> unreference ( $watcher );
$loop -> unreference ( $watcher );
});
$this -> assertFalse ( $invoked );
}
2018-06-18 20:00:01 +02:00
public function testRepeatWatcherUnrefRunResult ()
{
2016-06-03 17:01:21 +02:00
$invoked = false ;
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) use ( & $invoked ) {
$watcher = $loop -> repeat ( 2000 , function () use ( & $invoked ) {
2016-06-03 17:01:21 +02:00
$invoked = true ;
});
$loop -> unreference ( $watcher );
});
$this -> assertFalse ( $invoked );
}
2018-06-18 20:00:01 +02:00
public function testOnReadableWatcherUnrefRunResult ()
{
2017-03-14 00:52:57 +01:00
$invoked = false ;
$this -> start ( function ( Driver $loop ) use ( & $invoked ) {
$watcher = $loop -> onReadable ( STDIN , function () use ( & $invoked ) {
$invoked = true ;
2016-06-03 17:01:21 +02:00
});
$loop -> unreference ( $watcher );
});
2017-03-14 00:52:57 +01:00
$this -> assertFalse ( $invoked );
2016-06-03 17:01:21 +02:00
}
2018-06-18 20:00:01 +02:00
public function testOnWritableWatcherKeepAliveRunResult ()
{
2017-03-14 00:52:57 +01:00
$invoked = false ;
$this -> start ( function ( Driver $loop ) use ( & $invoked ) {
$watcher = $loop -> onWritable ( STDOUT , function () use ( & $invoked ) {
$invoked = true ;
2016-06-03 17:01:21 +02:00
});
$loop -> unreference ( $watcher );
});
2017-03-14 00:52:57 +01:00
$this -> assertFalse ( $invoked );
2016-06-03 17:01:21 +02:00
}
2018-06-18 20:00:01 +02:00
public function testOnSignalWatcherKeepAliveRunResult ()
{
2017-03-14 22:47:54 +01:00
$this -> checkForSignalCapability ();
$invoked = false ;
$this -> start ( function ( Driver $loop ) use ( & $invoked ) {
2017-03-10 21:31:57 +01:00
$watcher = $loop -> onSignal ( SIGUSR1 , function () {
2016-06-03 17:01:21 +02:00
// empty
});
2017-03-14 22:47:54 +01:00
$watcher = $loop -> delay ( 100 , function () use ( & $invoked , $loop , $watcher ) {
$invoked = true ;
$loop -> unreference ( $watcher );
});
2016-06-03 17:01:21 +02:00
$loop -> unreference ( $watcher );
});
2017-03-14 22:47:54 +01:00
$this -> assertTrue ( $invoked );
2016-06-03 17:01:21 +02:00
}
2018-06-18 20:00:01 +02:00
public function testUnreferencedDeferWatcherStillExecutes ()
{
2017-01-08 17:56:59 +01:00
$invoked = false ;
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) use ( & $invoked ) {
2017-01-08 17:56:59 +01:00
$watcher = $loop -> defer ( function () use ( & $invoked ) {
$invoked = true ;
});
$loop -> unreference ( $watcher );
$loop -> defer ( function () {
// just to keep loop running
});
});
$this -> assertTrue ( $invoked );
}
2018-06-18 20:00:01 +02:00
public function testLoopDoesNotBlockOnNegativeTimerExpiration ()
{
2017-01-08 17:56:59 +01:00
$invoked = false ;
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) use ( & $invoked ) {
2017-01-08 17:56:59 +01:00
$loop -> delay ( 1 , function () use ( & $invoked ) {
$invoked = true ;
});
2017-11-29 13:36:50 +01:00
\usleep ( 1000 * 10 );
2017-01-08 17:56:59 +01:00
});
$this -> assertTrue ( $invoked );
}
2018-06-18 20:00:01 +02:00
public function testDisabledDeferReenableInSubsequentTick ()
{
2016-06-03 17:01:21 +02:00
$this -> expectOutputString ( " 123 " );
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) {
2016-06-03 17:01:21 +02:00
$watcherId = $loop -> defer ( function ( $watcherId ) {
echo 3 ;
});
$loop -> disable ( $watcherId );
$loop -> defer ( function () use ( $loop , $watcherId ) {
$loop -> enable ( $watcherId );
echo 2 ;
});
echo 1 ;
});
}
2018-06-18 20:00:01 +02:00
public function provideRegistrationArgs ()
{
2016-06-03 17:01:21 +02:00
$args = [
2020-07-14 21:45:35 +02:00
[
" defer " ,
[
function () {
},
],
],
[
" delay " ,
[
5 ,
function () {
},
],
],
[
" repeat " ,
[
5 ,
function () {
},
],
],
[
" onWritable " ,
[
\STDOUT ,
function () {
},
],
],
[
" onReadable " ,
[
\STDIN ,
function () {
},
],
],
[
" onSignal " ,
[
\SIGUSR1 ,
function () {
},
],
],
2016-06-03 17:01:21 +02:00
];
return $args ;
}
2017-01-07 12:23:28 +01:00
2016-12-29 21:59:26 +01:00
/**
* @ requires PHP 7
* @ dataProvider provideRegistrationArgs
*/
2018-06-18 20:00:01 +02:00
public function testWeakTypes ( $type , $args )
{
2016-12-29 18:16:36 +01:00
if ( $type == " onSignal " ) {
2017-03-12 17:33:46 +01:00
$this -> checkForSignalCapability ();
2016-12-29 18:16:36 +01:00
}
2016-12-29 17:45:12 +01:00
$this -> start ( function ( Driver $loop ) use ( $type , $args , & $invoked ) {
if ( $type == " onReadable " ) {
2020-07-14 21:45:35 +02:00
$ends = \stream_socket_pair (
\stripos ( PHP_OS , " win " ) === 0 ? STREAM_PF_INET : STREAM_PF_UNIX ,
STREAM_SOCK_STREAM ,
STREAM_IPPROTO_IP
);
2017-11-29 13:36:50 +01:00
\fwrite ( $ends [ 0 ], " trigger readability watcher " );
2016-12-29 17:45:12 +01:00
$args = [ $ends [ 1 ]];
} else {
2017-11-29 13:36:50 +01:00
\array_pop ( $args );
2016-12-29 17:45:12 +01:00
}
$expectedData = 20.75 ;
2017-11-29 13:36:50 +01:00
if ( \substr ( $type , 0 , 2 ) == " on " ) {
2017-03-10 21:31:57 +01:00
$args [] = function ( $watcherId , $arg , int $data ) use ( $loop , & $invoked , $expectedData ) {
2016-12-29 17:45:12 +01:00
$invoked = true ;
$this -> assertSame (( int ) $expectedData , $data );
$loop -> unreference ( $watcherId );
};
} else {
2017-03-10 21:31:57 +01:00
$args [] = function ( $watcherId , int $data ) use ( $loop , & $invoked , $expectedData , $type ) {
2016-12-29 17:45:12 +01:00
$invoked = true ;
$this -> assertSame (( int ) $expectedData , $data );
if ( $type == " repeat " ) {
$loop -> unreference ( $watcherId );
}
};
}
$args [] = $expectedData ;
2017-11-29 13:36:50 +01:00
\call_user_func_array ([ $loop , $type ], $args );
2016-12-29 17:45:12 +01:00
if ( $type == " onSignal " ) {
2017-03-10 21:31:57 +01:00
$loop -> defer ( function () {
2016-12-29 17:45:12 +01:00
\posix_kill ( \getmypid (), \SIGUSR1 );
});
}
});
$this -> assertTrue ( $invoked );
}
2016-06-07 01:53:28 +02:00
/** @dataProvider provideRegistrationArgs */
2018-06-18 20:00:01 +02:00
public function testDisableWithConsecutiveCancel ( $type , $args )
{
2016-06-03 17:01:21 +02:00
if ( $type === " onSignal " ) {
2017-03-12 17:33:46 +01:00
$this -> checkForSignalCapability ();
2016-06-03 17:01:21 +02:00
}
2017-03-14 00:52:57 +01:00
$invoked = false ;
$this -> start ( function ( Driver $loop ) use ( & $invoked , $type , $args ) {
2016-06-03 17:01:21 +02:00
$func = [ $loop , $type ];
$watcherId = \call_user_func_array ( $func , $args );
$loop -> disable ( $watcherId );
2017-03-14 00:52:57 +01:00
$loop -> defer ( function () use ( & $invoked , $loop , $watcherId ) {
2016-06-03 17:01:21 +02:00
$loop -> cancel ( $watcherId );
2017-03-14 00:52:57 +01:00
$invoked = true ;
2016-06-03 17:01:21 +02:00
});
2017-03-14 00:52:57 +01:00
$this -> assertFalse ( $invoked );
2016-06-03 17:01:21 +02:00
});
2017-03-14 00:52:57 +01:00
$this -> assertTrue ( $invoked );
2016-06-03 17:01:21 +02:00
}
2016-06-07 01:53:28 +02:00
/** @dataProvider provideRegistrationArgs */
2018-06-18 20:00:01 +02:00
public function testWatcherReferenceInfo ( $type , $args )
{
2016-06-03 17:01:21 +02:00
if ( $type === " onSignal " ) {
2017-03-12 17:33:46 +01:00
$this -> checkForSignalCapability ();
2016-06-03 17:01:21 +02:00
}
$loop = $this -> loop ;
$func = [ $loop , $type ];
2017-11-29 13:36:50 +01:00
if ( \substr ( $type , 0 , 2 ) === " on " ) {
$type = " on_ " . \lcfirst ( \substr ( $type , 2 ));
2016-06-03 17:01:21 +02:00
}
// being referenced is the default
$watcherId1 = \call_user_func_array ( $func , $args );
2016-12-28 19:48:34 +01:00
$info = $loop -> getInfo ();
2016-06-03 17:01:21 +02:00
$expected = [ " enabled " => 1 , " disabled " => 0 ];
$this -> assertSame ( $expected , $info [ $type ]);
$expected = [ " referenced " => 1 , " unreferenced " => 0 ];
2017-03-10 22:20:28 +01:00
$this -> assertSame ( $expected , $info [ " enabled_watchers " ]);
2016-06-03 17:01:21 +02:00
// explicitly reference() even though it's the default setting
$argsCopy = $args ;
$watcherId2 = \call_user_func_array ( $func , $argsCopy );
$loop -> reference ( $watcherId2 );
$loop -> reference ( $watcherId2 );
2016-12-28 19:48:34 +01:00
$info = $loop -> getInfo ();
2016-06-03 17:01:21 +02:00
$expected = [ " enabled " => 2 , " disabled " => 0 ];
$this -> assertSame ( $expected , $info [ $type ]);
$expected = [ " referenced " => 2 , " unreferenced " => 0 ];
2017-03-10 22:20:28 +01:00
$this -> assertSame ( $expected , $info [ " enabled_watchers " ]);
2016-06-03 17:01:21 +02:00
// disabling a referenced watcher should decrement the referenced count
$loop -> disable ( $watcherId2 );
$loop -> disable ( $watcherId2 );
$loop -> disable ( $watcherId2 );
2016-12-28 19:48:34 +01:00
$info = $loop -> getInfo ();
2016-06-03 17:01:21 +02:00
$expected = [ " referenced " => 1 , " unreferenced " => 0 ];
2017-03-10 22:20:28 +01:00
$this -> assertSame ( $expected , $info [ " enabled_watchers " ]);
2016-06-03 17:01:21 +02:00
// enabling a referenced watcher should increment the referenced count
$loop -> enable ( $watcherId2 );
$loop -> enable ( $watcherId2 );
2016-12-28 19:48:34 +01:00
$info = $loop -> getInfo ();
2016-06-03 17:01:21 +02:00
$expected = [ " referenced " => 2 , " unreferenced " => 0 ];
2017-03-10 22:20:28 +01:00
$this -> assertSame ( $expected , $info [ " enabled_watchers " ]);
2016-06-03 17:01:21 +02:00
// cancelling an referenced watcher should decrement the referenced count
$loop -> cancel ( $watcherId2 );
2016-12-28 19:48:34 +01:00
$info = $loop -> getInfo ();
2016-06-03 17:01:21 +02:00
$expected = [ " referenced " => 1 , " unreferenced " => 0 ];
2017-03-10 22:20:28 +01:00
$this -> assertSame ( $expected , $info [ " enabled_watchers " ]);
2016-06-03 17:01:21 +02:00
// unreference() should just increment unreferenced count
$watcherId2 = \call_user_func_array ( $func , $args );
$loop -> unreference ( $watcherId2 );
2016-12-28 19:48:34 +01:00
$info = $loop -> getInfo ();
2016-06-03 17:01:21 +02:00
$expected = [ " enabled " => 2 , " disabled " => 0 ];
$this -> assertSame ( $expected , $info [ $type ]);
$expected = [ " referenced " => 1 , " unreferenced " => 1 ];
2017-03-10 22:20:28 +01:00
$this -> assertSame ( $expected , $info [ " enabled_watchers " ]);
2016-06-03 17:01:21 +02:00
$loop -> cancel ( $watcherId1 );
$loop -> cancel ( $watcherId2 );
}
2016-06-07 01:53:28 +02:00
/** @dataProvider provideRegistrationArgs */
2018-06-18 20:00:01 +02:00
public function testWatcherRegistrationAndCancellationInfo ( $type , $args )
{
2016-06-03 17:01:21 +02:00
if ( $type === " onSignal " ) {
2017-03-12 17:33:46 +01:00
$this -> checkForSignalCapability ();
2016-06-03 17:01:21 +02:00
}
$loop = $this -> loop ;
$func = [ $loop , $type ];
2017-11-29 13:36:50 +01:00
if ( \substr ( $type , 0 , 2 ) === " on " ) {
$type = " on_ " . \lcfirst ( \substr ( $type , 2 ));
2016-06-03 17:01:21 +02:00
}
$watcherId = \call_user_func_array ( $func , $args );
$this -> assertInternalType ( " string " , $watcherId );
2016-12-28 19:48:34 +01:00
$info = $loop -> getInfo ();
2016-06-03 17:01:21 +02:00
$expected = [ " enabled " => 1 , " disabled " => 0 ];
$this -> assertSame ( $expected , $info [ $type ]);
// invoke enable() on active watcher to ensure it has no side-effects
$loop -> enable ( $watcherId );
2016-12-28 19:48:34 +01:00
$info = $loop -> getInfo ();
2016-06-03 17:01:21 +02:00
$expected = [ " enabled " => 1 , " disabled " => 0 ];
$this -> assertSame ( $expected , $info [ $type ]);
// invoke disable() twice to ensure it has no side-effects
$loop -> disable ( $watcherId );
$loop -> disable ( $watcherId );
2016-12-28 19:48:34 +01:00
$info = $loop -> getInfo ();
2016-06-03 17:01:21 +02:00
$expected = [ " enabled " => 0 , " disabled " => 1 ];
$this -> assertSame ( $expected , $info [ $type ]);
$loop -> cancel ( $watcherId );
2016-12-28 19:48:34 +01:00
$info = $loop -> getInfo ();
2016-06-03 17:01:21 +02:00
$expected = [ " enabled " => 0 , " disabled " => 0 ];
$this -> assertSame ( $expected , $info [ $type ]);
$watcherId = \call_user_func_array ( $func , $args );
2016-12-28 19:48:34 +01:00
$info = $loop -> getInfo ();
2016-06-03 17:01:21 +02:00
$expected = [ " enabled " => 1 , " disabled " => 0 ];
$this -> assertSame ( $expected , $info [ $type ]);
$loop -> disable ( $watcherId );
2016-12-28 19:48:34 +01:00
$info = $loop -> getInfo ();
2016-06-03 17:01:21 +02:00
$expected = [ " enabled " => 0 , " disabled " => 1 ];
$this -> assertSame ( $expected , $info [ $type ]);
$loop -> enable ( $watcherId );
2016-12-28 19:48:34 +01:00
$info = $loop -> getInfo ();
2016-06-03 17:01:21 +02:00
$expected = [ " enabled " => 1 , " disabled " => 0 ];
$this -> assertSame ( $expected , $info [ $type ]);
$loop -> cancel ( $watcherId );
2016-12-28 19:48:34 +01:00
$info = $loop -> getInfo ();
2016-06-03 17:01:21 +02:00
$expected = [ " enabled " => 0 , " disabled " => 0 ];
$this -> assertSame ( $expected , $info [ $type ]);
2016-08-30 22:39:47 +02:00
$loop -> disable ( $watcherId );
2016-12-28 19:48:34 +01:00
$info = $loop -> getInfo ();
2016-08-30 22:39:47 +02:00
$expected = [ " enabled " => 0 , " disabled " => 0 ];
$this -> assertSame ( $expected , $info [ $type ]);
2016-06-03 17:01:21 +02:00
}
2017-04-26 20:27:59 +02:00
/**
* @ dataProvider provideRegistrationArgs
* @ group memoryleak
*/
2018-06-18 20:00:01 +02:00
public function testNoMemoryLeak ( $type , $args )
{
2017-03-10 21:31:57 +01:00
if ( $this -> getTestResultObject () -> getCollectCodeCoverageInformation ()) {
2016-12-29 17:45:12 +01:00
$this -> markTestSkipped ( " Cannot run this test with code coverage active [code coverage consumes memory which makes it impossible to rely on memory_get_usage()] " );
}
2016-08-15 15:08:09 +02:00
2016-06-07 19:45:15 +02:00
$runs = 2000 ;
2016-06-03 17:01:21 +02:00
if ( $type === " onSignal " ) {
2017-03-12 17:33:46 +01:00
$this -> checkForSignalCapability ();
2016-06-03 17:01:21 +02:00
}
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) use ( $type , $args , $runs ) {
2017-11-29 13:36:50 +01:00
$initialMem = \memory_get_usage ();
2016-06-03 17:01:21 +02:00
$cb = function ( $runs ) use ( $loop , $type , $args ) {
$func = [ $loop , $type ];
for ( $watchers = [], $i = 0 ; $i < $runs ; $i ++ ) {
$watchers [] = \call_user_func_array ( $func , $args );
}
foreach ( $watchers as $watcher ) {
$loop -> cancel ( $watcher );
}
for ( $watchers = [], $i = 0 ; $i < $runs ; $i ++ ) {
$watchers [] = \call_user_func_array ( $func , $args );
}
foreach ( $watchers as $watcher ) {
$loop -> disable ( $watcher );
$loop -> cancel ( $watcher );
}
for ( $watchers = [], $i = 0 ; $i < $runs ; $i ++ ) {
$watchers [] = \call_user_func_array ( $func , $args );
}
if ( $type === " repeat " ) {
$loop -> delay ( $msInterval = 7 , function () use ( $loop , $watchers ) {
foreach ( $watchers as $watcher ) {
$loop -> cancel ( $watcher );
}
});
} elseif ( $type !== " defer " && $type !== " delay " ) {
$loop -> defer ( function () use ( $loop , $watchers ) {
foreach ( $watchers as $watcher ) {
$loop -> cancel ( $watcher );
}
});
}
$loop -> run ();
if ( $type === " defer " ) {
$loop -> defer ( $fn = function ( $watcherId , $i ) use ( & $fn , $loop ) {
if ( $i ) {
$loop -> defer ( $fn , -- $i );
}
}, $runs );
$loop -> run ();
}
if ( $type === " delay " ) {
$loop -> delay ( $msDelay = 0 , $fn = function ( $watcherId , $i ) use ( & $fn , $loop ) {
if ( $i ) {
$loop -> delay ( $msDelay = 0 , $fn , -- $i );
}
}, $runs );
$loop -> run ();
}
if ( $type === " repeat " ) {
$loop -> repeat ( $msDelay = 0 , $fn = function ( $watcherId , $i ) use ( & $fn , $loop ) {
$loop -> cancel ( $watcherId );
if ( $i ) {
$loop -> repeat ( $msDelay = 0 , $fn , -- $i );
}
}, $runs );
$loop -> run ();
}
if ( $type === " onWritable " ) {
2017-03-10 21:31:57 +01:00
$loop -> defer ( function ( $watcherId , $runs ) use ( $loop ) {
2016-06-03 17:01:21 +02:00
$fn = function ( $watcherId , $socket , $i ) use ( & $fn , $loop ) {
$loop -> cancel ( $watcherId );
if ( $socket ) {
2017-11-29 13:36:50 +01:00
\fwrite ( $socket , " . " );
2016-06-03 17:01:21 +02:00
}
if ( $i ) {
// explicitly use *different* streams with *different* resource ids
2020-07-14 21:45:35 +02:00
$ends = \stream_socket_pair (
\stripos (
PHP_OS ,
" win "
) === 0 ? STREAM_PF_INET : STREAM_PF_UNIX ,
STREAM_SOCK_STREAM ,
STREAM_IPPROTO_IP
);
2016-06-03 17:01:21 +02:00
$loop -> onWritable ( $ends [ 0 ], $fn , -- $i );
2017-03-10 21:31:57 +01:00
$loop -> onReadable ( $ends [ 1 ], function ( $watcherId ) use ( $loop ) {
2016-06-03 17:01:21 +02:00
$loop -> cancel ( $watcherId );
});
}
};
$fn ( $watcherId , null , $runs );
}, $runs + 1 );
$loop -> run ();
}
if ( $type === " onSignal " ) {
2016-06-07 20:21:10 +02:00
$sendSignal = function () {
\posix_kill ( \getmypid (), \SIGUSR1 );
};
$loop -> onSignal ( \SIGUSR1 , $fn = function ( $watcherId , $signo , $i ) use ( & $fn , $loop , $sendSignal ) {
2016-06-03 17:01:21 +02:00
if ( $i ) {
2016-06-07 20:21:10 +02:00
$loop -> onSignal ( \SIGUSR1 , $fn , -- $i );
$loop -> defer ( $sendSignal );
2016-06-03 17:01:21 +02:00
}
2016-06-07 20:21:10 +02:00
$loop -> cancel ( $watcherId );
}, $runs );
$loop -> defer ( $sendSignal );
2016-06-03 17:01:21 +02:00
$loop -> run ();
}
};
2017-11-29 13:36:50 +01:00
$closureMem = \memory_get_usage () - $initialMem ;
2016-06-07 19:45:15 +02:00
$cb ( $runs ); /* just to set up eventual structures inside loop without counting towards memory comparison */
2017-11-29 13:36:50 +01:00
\gc_collect_cycles ();
$initialMem = \memory_get_usage () - $closureMem ;
2016-06-07 19:45:15 +02:00
$cb ( $runs );
2016-06-03 17:01:21 +02:00
unset ( $cb );
2017-11-29 13:36:50 +01:00
\gc_collect_cycles ();
$endMem = \memory_get_usage ();
2016-06-03 17:01:21 +02:00
/* this is allowing some memory usage due to runtime caches etc., but nothing actually leaking */
2016-06-07 19:45:15 +02:00
$this -> assertLessThan ( $runs * 4 , $endMem - $initialMem ); // * 4, as 4 is minimal sizeof(void *)
2016-06-03 17:01:21 +02:00
});
}
2017-03-25 19:52:17 +01:00
/**
* The first number of each tuple indicates the tick in which the watcher is supposed to execute , the second digit
* indicates the order within the tick .
*/
2018-06-18 20:00:01 +02:00
public function testExecutionOrderGuarantees ()
{
2020-07-14 21:45:35 +02:00
$this -> expectOutputString ( " 01 02 03 04 " . \str_repeat ( " 05 " , 8 ) . " 10 11 12 " . \str_repeat (
" 13 " ,
4
) . " 20 " . \str_repeat ( " 21 " , 4 ) . " 30 40 41 " );
2017-12-05 08:48:36 +01:00
$this -> start ( function ( Driver $loop ) {
2017-05-02 21:42:45 +02:00
// Wrap in extra defer, so driver creation time doesn't count for timers, as timers are driver creation
// relative instead of last tick relative before first tick.
2017-12-05 08:48:36 +01:00
$loop -> defer ( function () use ( $loop ) {
2017-05-02 21:42:45 +02:00
$f = function () use ( $loop ) {
2017-11-29 13:36:50 +01:00
$args = \func_get_args ();
2017-05-02 21:42:45 +02:00
return function ( $watcherId ) use ( $loop , & $args ) {
if ( ! $args ) {
$this -> fail ( " Watcher callback called too often " );
}
$loop -> cancel ( $watcherId );
2017-11-29 13:36:50 +01:00
echo \array_shift ( $args ) . \array_shift ( $args ), " " ;
2017-05-02 21:42:45 +02:00
};
2016-06-07 01:53:28 +02:00
};
2017-05-02 21:42:45 +02:00
$loop -> onWritable ( STDOUT , $f ( 0 , 5 ));
$writ1 = $loop -> onWritable ( STDOUT , $f ( 0 , 5 ));
$writ2 = $loop -> onWritable ( STDOUT , $f ( 0 , 5 ));
$loop -> delay ( $msDelay = 0 , $f ( 0 , 5 ));
$del1 = $loop -> delay ( $msDelay = 0 , $f ( 0 , 5 ));
$del2 = $loop -> delay ( $msDelay = 0 , $f ( 0 , 5 ));
$del3 = $loop -> delay ( $msDelay = 0 , $f ());
$del4 = $loop -> delay ( $msDelay = 0 , $f ( 1 , 3 ));
$del5 = $loop -> delay ( $msDelay = 0 , $f ( 2 , 0 ));
$loop -> defer ( function () use ( $loop , $del5 ) {
$loop -> disable ( $del5 );
});
$loop -> cancel ( $del3 );
$loop -> disable ( $del1 );
$loop -> disable ( $del2 );
$writ3 = $loop -> onWritable ( STDOUT , $f ());
$loop -> cancel ( $writ3 );
$loop -> disable ( $writ1 );
$loop -> disable ( $writ2 );
$loop -> enable ( $writ1 );
$writ4 = $loop -> onWritable ( STDOUT , $f ( 1 , 3 ));
$loop -> onWritable ( STDOUT , $f ( 0 , 5 ));
$loop -> enable ( $writ2 );
$loop -> disable ( $writ4 );
$loop -> defer ( function () use ( $loop , $writ4 , $f ) {
$loop -> enable ( $writ4 );
$loop -> onWritable ( STDOUT , $f ( 1 , 3 ));
});
2016-06-07 01:53:28 +02:00
2017-05-02 21:42:45 +02:00
$loop -> enable ( $del1 );
$loop -> delay ( $msDelay = 0 , $f ( 0 , 5 ));
$loop -> enable ( $del2 );
$loop -> disable ( $del4 );
$loop -> defer ( function () use ( $loop , $del4 , $f ) {
$loop -> enable ( $del4 );
$loop -> onWritable ( STDOUT , $f ( 1 , 3 ));
});
2017-01-07 12:23:28 +01:00
2017-05-02 21:42:45 +02:00
$loop -> delay ( $msDelay = 1000 , $f ( 4 , 1 ));
$loop -> delay ( $msDelay = 600 , $f ( 3 , 0 ));
$loop -> delay ( $msDelay = 500 , $f ( 2 , 1 ));
$loop -> repeat ( $msDelay = 500 , $f ( 2 , 1 ));
2017-05-03 22:21:57 +02:00
$rep1 = $loop -> repeat ( $msDelay = 250 , $f ( 2 , 1 ));
2017-05-02 21:42:45 +02:00
$loop -> disable ( $rep1 );
$loop -> delay ( $msDelay = 500 , $f ( 2 , 1 ));
$loop -> enable ( $rep1 );
$loop -> defer ( $f ( 0 , 1 ));
$def1 = $loop -> defer ( $f ( 0 , 3 ));
$def2 = $loop -> defer ( $f ( 1 , 1 ));
$def3 = $loop -> defer ( $f ());
$loop -> defer ( $f ( 0 , 2 ));
$loop -> disable ( $def1 );
$loop -> cancel ( $def3 );
$loop -> enable ( $def1 );
2017-12-05 08:48:36 +01:00
$loop -> defer ( function () use ( $loop , $def2 , $del5 , $f ) {
2017-05-02 21:42:45 +02:00
$tick = $f ( 0 , 4 );
$tick ( " invalid " );
$loop -> defer ( $f ( 1 , 0 ));
$loop -> enable ( $def2 );
$loop -> defer ( $f ( 1 , 2 ));
$loop -> defer ( function () use ( $loop , $del5 , $f ) {
$loop -> enable ( $del5 );
2016-06-26 17:12:33 +02:00
$loop -> defer ( function () use ( $loop , $f ) {
2017-11-29 13:36:50 +01:00
\usleep ( 700000 ); // to have $msDelay == 500 and $msDelay == 600 run at the same tick (but not $msDelay == 150)
2017-05-02 21:42:45 +02:00
$loop -> defer ( function () use ( $loop , $f ) {
$loop -> defer ( $f ( 4 , 0 ));
});
2016-06-26 17:12:33 +02:00
});
});
});
2017-05-02 21:42:45 +02:00
$loop -> disable ( $def2 );
2016-06-07 01:53:28 +02:00
});
});
}
2018-06-18 20:00:01 +02:00
public function testSignalExecutionOrder ()
{
2017-03-14 22:47:54 +01:00
$this -> checkForSignalCapability ();
2016-06-07 01:53:28 +02:00
2016-10-26 15:39:07 +02:00
$this -> expectOutputString ( " 122222 " );
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) {
$f = function ( $i ) use ( $loop ) {
return function ( $watcherId ) use ( $loop , $i ) {
2016-06-07 01:53:28 +02:00
$loop -> cancel ( $watcherId );
echo $i ;
};
};
$loop -> defer ( $f ( 1 ));
$loop -> onSignal ( SIGUSR1 , $f ( 2 ));
2016-10-26 15:39:07 +02:00
$sig1 = $loop -> onSignal ( SIGUSR1 , $f ( 2 ));
$sig2 = $loop -> onSignal ( SIGUSR1 , $f ( 2 ));
2016-06-07 01:53:28 +02:00
$sig3 = $loop -> onSignal ( SIGUSR1 , $f ( " FAIL - MUST NOT BE CALLED " ));
$loop -> disable ( $sig1 );
2016-10-26 15:39:07 +02:00
$loop -> onSignal ( SIGUSR1 , $f ( 2 ));
2016-06-07 01:53:28 +02:00
$loop -> disable ( $sig2 );
$loop -> enable ( $sig1 );
$loop -> cancel ( $sig3 );
2016-10-26 15:39:07 +02:00
$loop -> onSignal ( SIGUSR1 , $f ( 2 ));
2017-03-10 21:31:57 +01:00
$loop -> defer ( function () use ( $loop , $sig2 ) {
2016-06-07 01:53:28 +02:00
$loop -> enable ( $sig2 );
2017-03-10 21:31:57 +01:00
$loop -> defer ( function () use ( $loop ) {
2016-06-07 19:31:49 +02:00
\posix_kill ( \getmypid (), \SIGUSR1 );
2017-03-10 21:31:57 +01:00
$loop -> delay ( $msDelay = 10 , function () use ( $loop ) {
2016-06-07 19:31:49 +02:00
$loop -> stop ();
});
2016-06-07 01:53:28 +02:00
});
});
});
}
2017-03-14 17:50:24 +01:00
/** @expectedException \Amp\Loop\InvalidWatcherError */
2018-06-18 20:00:01 +02:00
public function testExceptionOnEnableNonexistentWatcher ()
{
2016-12-23 01:43:09 +01:00
try {
$this -> loop -> enable ( " nonexistentWatcher " );
2017-03-14 17:50:24 +01:00
} catch ( InvalidWatcherError $e ) {
2017-04-24 15:39:08 +02:00
$this -> assertSame ( " nonexistentWatcher " , $e -> getWatcherId ());
2016-12-23 01:43:09 +01:00
throw $e ;
}
2016-06-03 17:01:21 +02:00
}
2018-06-18 20:00:01 +02:00
public function testSuccessOnDisableNonexistentWatcher ()
{
2016-06-03 17:01:21 +02:00
$this -> loop -> disable ( " nonexistentWatcher " );
2017-03-12 17:33:46 +01:00
// Otherwise risky, throwing fails the test
$this -> assertTrue ( true );
2016-06-03 17:01:21 +02:00
}
2018-06-18 20:00:01 +02:00
public function testSuccessOnCancelNonexistentWatcher ()
{
2016-06-03 17:01:21 +02:00
$this -> loop -> cancel ( " nonexistentWatcher " );
2017-03-12 17:33:46 +01:00
// Otherwise risky, throwing fails the test
$this -> assertTrue ( true );
2016-06-03 17:01:21 +02:00
}
2017-03-14 17:50:24 +01:00
/** @expectedException \Amp\Loop\InvalidWatcherError */
2018-06-18 20:00:01 +02:00
public function testExceptionOnReferenceNonexistentWatcher ()
{
2016-12-23 01:43:09 +01:00
try {
$this -> loop -> reference ( " nonexistentWatcher " );
2017-03-14 17:50:24 +01:00
} catch ( InvalidWatcherError $e ) {
2017-04-24 15:39:08 +02:00
$this -> assertSame ( " nonexistentWatcher " , $e -> getWatcherId ());
2016-12-23 01:43:09 +01:00
throw $e ;
}
2016-06-07 01:53:28 +02:00
}
2018-06-18 20:00:01 +02:00
public function testSuccessOnUnreferenceNonexistentWatcher ()
{
2018-04-12 09:46:52 +02:00
$this -> loop -> unreference ( " nonexistentWatcher " );
// Otherwise risky, throwing fails the test
$this -> assertTrue ( true );
2016-06-07 01:53:28 +02:00
}
2017-03-14 17:50:24 +01:00
/** @expectedException \Amp\Loop\InvalidWatcherError */
2018-06-18 20:00:01 +02:00
public function testWatcherInvalidityOnDefer ()
{
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) {
$loop -> defer ( function ( $watcher ) use ( $loop ) {
2016-08-30 22:39:47 +02:00
$loop -> enable ( $watcher );
2016-06-07 01:53:28 +02:00
});
});
}
2017-03-14 17:50:24 +01:00
/** @expectedException \Amp\Loop\InvalidWatcherError */
2018-06-18 20:00:01 +02:00
public function testWatcherInvalidityOnDelay ()
{
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) {
$loop -> delay ( $msDelay = 0 , function ( $watcher ) use ( $loop ) {
2016-08-30 22:39:47 +02:00
$loop -> enable ( $watcher );
2016-06-07 01:53:28 +02:00
});
});
}
2018-06-18 20:00:01 +02:00
public function testEventsNotExecutedInSameTickAsEnabled ()
{
2016-06-07 01:53:28 +02:00
$this -> start ( function ( Driver $loop ) {
2017-03-10 21:31:57 +01:00
$loop -> defer ( function () use ( $loop ) {
$loop -> defer ( function () use ( $loop , & $diswatchers , & $watchers ) {
$loop -> defer ( function () use ( $loop , $diswatchers ) {
2016-06-07 01:53:28 +02:00
foreach ( $diswatchers as $watcher ) {
$loop -> disable ( $watcher );
}
2017-03-10 21:31:57 +01:00
$loop -> defer ( function () use ( $loop , $diswatchers ) {
$loop -> defer ( function () use ( $loop , $diswatchers ) {
2016-06-07 01:53:28 +02:00
foreach ( $diswatchers as $watcher ) {
$loop -> cancel ( $watcher );
}
});
2016-06-07 19:25:59 +02:00
foreach ( $diswatchers as $watcher ) {
$loop -> enable ( $watcher );
}
2016-06-07 01:53:28 +02:00
});
});
2016-06-07 19:25:59 +02:00
foreach ( $watchers as $watcher ) {
$loop -> cancel ( $watcher );
}
foreach ( $diswatchers as $watcher ) {
$loop -> disable ( $watcher );
$loop -> enable ( $watcher );
}
2016-06-07 01:53:28 +02:00
});
2017-03-10 21:31:57 +01:00
$f = function () use ( $loop ) {
2016-06-07 01:53:28 +02:00
$watchers [] = $loop -> defer ([ $this , " fail " ]);
$watchers [] = $loop -> delay ( $msDelay = 0 , [ $this , " fail " ]);
$watchers [] = $loop -> repeat ( $msDelay = 0 , [ $this , " fail " ]);
$watchers [] = $loop -> onWritable ( STDIN , [ $this , " fail " ]);
return $watchers ;
};
$watchers = $f ();
$diswatchers = $f ();
});
});
2017-03-12 17:33:46 +01:00
// Otherwise risky, as we only rely on $this->fail()
$this -> assertTrue ( true );
2016-06-07 01:53:28 +02:00
}
2018-06-18 20:00:01 +02:00
public function testEnablingWatcherAllowsSubsequentInvocation ()
{
2016-06-03 17:01:21 +02:00
$loop = $this -> loop ;
$increment = 0 ;
2017-03-10 21:31:57 +01:00
$watcherId = $loop -> defer ( function () use ( & $increment ) {
2016-06-03 17:01:21 +02:00
$increment ++ ;
});
$loop -> disable ( $watcherId );
$loop -> delay ( $msDelay = 5 , [ $loop , " stop " ]);
$loop -> run ();
2017-04-24 15:39:08 +02:00
$this -> assertSame ( 0 , $increment );
2016-06-03 17:01:21 +02:00
$loop -> enable ( $watcherId );
$loop -> delay ( $msDelay = 5 , [ $loop , " stop " ]);
$loop -> run ();
2017-04-24 15:39:08 +02:00
$this -> assertSame ( 1 , $increment );
2016-06-03 17:01:21 +02:00
}
2018-06-18 20:00:01 +02:00
public function testUnresolvedEventsAreReenabledOnRunFollowingPreviousStop ()
{
2016-06-03 17:01:21 +02:00
$increment = 0 ;
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) use ( & $increment ) {
2016-06-03 17:01:21 +02:00
$loop -> defer ([ $loop , " stop " ]);
$loop -> run ();
$loop -> defer ( function () use ( & $increment , $loop ) {
$loop -> delay ( $msDelay = 100 , function () use ( $loop , & $increment ) {
$increment ++ ;
$loop -> stop ();
});
});
2017-04-24 15:39:08 +02:00
$this -> assertSame ( 0 , $increment );
2016-06-03 17:01:21 +02:00
\usleep ( 5000 );
});
2017-04-24 15:39:08 +02:00
$this -> assertSame ( 1 , $increment );
2016-06-03 17:01:21 +02:00
}
2018-06-18 20:00:01 +02:00
public function testTimerWatcherParameterOrder ()
{
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) {
2016-06-03 17:01:21 +02:00
$counter = 0 ;
$loop -> defer ( function ( $watcherId ) use ( $loop , & $counter ) {
$this -> assertInternalType ( " string " , $watcherId );
if ( ++ $counter === 3 ) {
$loop -> stop ();
}
});
$loop -> delay ( $msDelay = 5 , function ( $watcherId ) use ( $loop , & $counter ) {
$this -> assertInternalType ( " string " , $watcherId );
if ( ++ $counter === 3 ) {
$loop -> stop ();
}
});
$loop -> repeat ( $msDelay = 5 , function ( $watcherId ) use ( $loop , & $counter ) {
$this -> assertInternalType ( " string " , $watcherId );
$loop -> cancel ( $watcherId );
if ( ++ $counter === 3 ) {
$loop -> stop ();
}
});
});
}
2018-06-18 20:00:01 +02:00
public function testStreamWatcherParameterOrder ()
{
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) use ( & $invoked ) {
2016-06-03 17:01:21 +02:00
$invoked = 0 ;
$loop -> onWritable ( STDOUT , function ( $watcherId , $stream ) use ( $loop , & $invoked ) {
$this -> assertInternalType ( " string " , $watcherId );
$this -> assertSame ( STDOUT , $stream );
$invoked ++ ;
$loop -> cancel ( $watcherId );
});
});
$this -> assertSame ( 1 , $invoked );
}
2018-06-18 20:00:01 +02:00
public function testDisablingWatcherPreventsSubsequentInvocation ()
{
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) {
2016-06-03 17:01:21 +02:00
$increment = 0 ;
$watcherId = $loop -> defer ( function () use ( & $increment ) {
$increment ++ ;
});
$loop -> disable ( $watcherId );
$loop -> delay ( $msDelay = 5 , [ $loop , " stop " ]);
2017-04-24 15:39:08 +02:00
$this -> assertSame ( 0 , $increment );
2016-06-03 17:01:21 +02:00
});
}
2018-06-18 20:00:01 +02:00
public function testImmediateExecution ()
{
2016-06-03 17:01:21 +02:00
$loop = $this -> loop ;
$increment = 0 ;
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) use ( & $increment ) {
2016-06-03 17:01:21 +02:00
$loop -> defer ( function () use ( & $increment ) {
$increment ++ ;
});
$loop -> defer ([ $loop , " stop " ]);
});
2017-04-24 15:39:08 +02:00
$this -> assertSame ( 1 , $increment );
2016-06-03 17:01:21 +02:00
}
2018-06-18 20:00:01 +02:00
public function testImmediatelyCallbacksDoNotRecurseInSameTick ()
{
2016-06-03 17:01:21 +02:00
$increment = 0 ;
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) use ( & $increment ) {
2016-06-03 17:01:21 +02:00
$loop -> defer ( function () use ( $loop , & $increment ) {
$increment ++ ;
$loop -> defer ( function () use ( & $increment ) {
$increment ++ ;
});
});
$loop -> defer ([ $loop , " stop " ]);
});
2017-04-24 15:39:08 +02:00
$this -> assertSame ( 1 , $increment );
2016-06-03 17:01:21 +02:00
}
2018-06-18 20:00:01 +02:00
public function testRunExecutesEventsUntilExplicitlyStopped ()
{
2016-06-03 17:01:21 +02:00
$increment = 0 ;
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) use ( & $increment ) {
2016-06-03 17:01:21 +02:00
$loop -> repeat ( $msInterval = 5 , function ( $watcherId ) use ( $loop , & $increment ) {
$increment ++ ;
if ( $increment === 10 ) {
$loop -> cancel ( $watcherId );
}
});
});
2017-04-24 15:39:08 +02:00
$this -> assertSame ( 10 , $increment );
2016-06-03 17:01:21 +02:00
}
/**
* @ expectedException \Exception
* @ expectedExceptionMessage loop error
*/
2018-06-18 20:00:01 +02:00
public function testLoopAllowsExceptionToBubbleUpDuringStart ()
{
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) {
$loop -> defer ( function () {
2016-06-03 17:01:21 +02:00
throw new \Exception ( " loop error " );
});
});
2016-05-24 10:37:25 +02:00
}
/**
* @ expectedException \RuntimeException
2016-06-03 17:01:21 +02:00
* @ expectedExceptionMessage test
2016-05-24 10:37:25 +02:00
*/
2018-06-18 20:00:01 +02:00
public function testLoopAllowsExceptionToBubbleUpFromRepeatingAlarmDuringStart ()
{
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) {
2016-06-03 17:01:21 +02:00
$loop -> repeat ( $msInterval = 1 , function () {
throw new \RuntimeException ( " test " );
});
});
}
2016-05-24 10:37:25 +02:00
2018-06-18 20:00:01 +02:00
public function testErrorHandlerCapturesUncaughtException ()
{
2016-06-03 17:01:21 +02:00
$msg = " " ;
2017-03-10 21:31:57 +01:00
$this -> loop -> setErrorHandler ( $f = function () {
});
$oldErrorHandler = $this -> loop -> setErrorHandler ( function ( \Exception $error ) use ( & $msg ) {
2016-06-03 17:01:21 +02:00
$msg = $error -> getMessage ();
});
2017-04-24 15:39:08 +02:00
$this -> assertSame ( $f , $oldErrorHandler );
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) {
$loop -> defer ( function () {
2016-06-03 17:01:21 +02:00
throw new \Exception ( " loop error " );
});
});
$this -> assertSame ( " loop error " , $msg );
}
2016-05-24 10:37:25 +02:00
2016-06-03 17:01:21 +02:00
/**
* @ expectedException \Exception
* @ expectedExceptionMessage errorception
*/
2018-06-18 20:00:01 +02:00
public function testOnErrorFailure ()
{
2017-03-10 21:31:57 +01:00
$this -> loop -> setErrorHandler ( function () {
2016-06-03 17:01:21 +02:00
throw new \Exception ( " errorception " );
});
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) {
$loop -> delay ( $msDelay = 5 , function () {
2016-06-03 17:01:21 +02:00
throw new \Exception ( " error " );
});
});
}
2016-05-24 10:37:25 +02:00
2016-06-03 17:01:21 +02:00
/**
* @ expectedException \RuntimeException
* @ expectedExceptionMessage test
*/
2018-06-18 20:00:01 +02:00
public function testLoopException ()
{
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) {
$loop -> defer ( function () use ( $loop ) {
2016-06-03 17:01:21 +02:00
// force next tick, outside of primary startup tick
2017-03-10 21:31:57 +01:00
$loop -> defer ( function () {
2016-06-03 17:01:21 +02:00
throw new \RuntimeException ( " test " );
});
});
});
}
2018-06-18 20:00:01 +02:00
public function testOnSignalWatcher ()
{
2017-03-14 22:47:54 +01:00
$this -> checkForSignalCapability ();
2016-06-03 17:01:21 +02:00
$this -> expectOutputString ( " caught SIGUSR1 " );
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) {
$loop -> delay ( $msDelay = 1 , function () use ( $loop ) {
2016-06-03 17:01:21 +02:00
\posix_kill ( \getmypid (), \SIGUSR1 );
2017-03-10 21:31:57 +01:00
$loop -> delay ( $msDelay = 10 , function () use ( $loop ) {
2016-06-03 17:01:21 +02:00
$loop -> stop ();
});
});
2017-03-10 21:31:57 +01:00
$loop -> onSignal ( SIGUSR1 , function ( $watcherId ) use ( $loop ) {
2016-06-03 17:01:21 +02:00
$loop -> cancel ( $watcherId );
echo " caught SIGUSR1 " ;
});
});
}
2018-06-18 20:00:01 +02:00
public function testInitiallyDisabledOnSignalWatcher ()
{
2017-03-14 22:47:54 +01:00
$this -> checkForSignalCapability ();
2016-06-03 17:01:21 +02:00
$this -> expectOutputString ( " caught SIGUSR1 " );
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) {
2017-03-25 21:47:30 +01:00
$stop = $loop -> delay ( $msDelay = 100 , function () use ( $loop ) {
2016-06-26 17:12:33 +02:00
echo " ERROR: manual stop " ;
$loop -> stop ();
});
2017-03-10 21:31:57 +01:00
$watcherId = $loop -> onSignal ( SIGUSR1 , function ( $watcherId ) use ( $loop , $stop ) {
2016-06-03 17:01:21 +02:00
echo " caught SIGUSR1 " ;
2016-06-26 17:12:33 +02:00
$loop -> disable ( $stop );
$loop -> disable ( $watcherId );
});
$loop -> disable ( $watcherId );
2017-03-10 21:31:57 +01:00
$loop -> delay ( $msDelay = 1 , function () use ( $loop , $watcherId ) {
2016-06-26 17:12:33 +02:00
$loop -> enable ( $watcherId );
2017-03-10 21:31:57 +01:00
$loop -> delay ( $msDelay = 1 , function () {
2016-08-14 21:14:43 +02:00
\posix_kill ( \getmypid (), SIGUSR1 );
2016-06-26 17:12:33 +02:00
});
});
});
}
2018-06-18 20:00:01 +02:00
public function testNestedLoopSignalDispatch ()
{
2017-03-14 22:47:54 +01:00
$this -> checkForSignalCapability ();
2016-06-26 17:12:33 +02:00
2016-08-14 21:14:43 +02:00
$this -> expectOutputString ( " inner SIGUSR2 \n outer SIGUSR1 \n " );
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) {
2017-05-19 16:47:55 +02:00
$loop -> delay ( $msDelay = 300 , function () use ( $loop ) {
2016-06-26 17:12:33 +02:00
$loop -> stop ();
});
$loop -> onSignal ( SIGUSR1 , function () use ( $loop ) {
echo " outer SIGUSR1 \n " ;
2016-06-03 17:01:21 +02:00
$loop -> stop ();
2016-06-26 17:12:33 +02:00
});
2016-06-03 17:01:21 +02:00
2016-06-26 17:12:33 +02:00
$loop -> delay ( $msDelay = 1 , function () {
2017-03-10 22:20:28 +01:00
/** @var Driver $loop */
$loop = ( $this -> getFactory ())();
2017-05-19 16:47:55 +02:00
$stop = $loop -> delay ( $msDelay = 100 , function () use ( $loop ) {
2016-06-26 17:12:33 +02:00
echo " ERROR: manual stop " ;
$loop -> stop ();
});
2016-08-14 21:14:43 +02:00
$loop -> onSignal ( SIGUSR2 , function ( $watcherId ) use ( $loop , $stop ) {
echo " inner SIGUSR2 \n " ;
2016-06-26 17:12:33 +02:00
$loop -> cancel ( $stop );
$loop -> cancel ( $watcherId );
});
$loop -> delay ( $msDelay = 1 , function () {
2016-08-14 21:14:43 +02:00
\posix_kill ( \getmypid (), SIGUSR2 );
2016-06-03 17:01:21 +02:00
});
2016-06-26 17:12:33 +02:00
$loop -> run ();
});
$loop -> delay ( $msDelay = 20 , function () {
\posix_kill ( \getmypid (), \SIGUSR1 );
2016-06-03 17:01:21 +02:00
});
2016-05-24 10:37:25 +02:00
});
}
2018-06-18 20:00:01 +02:00
public function testCancelRemovesWatcher ()
{
2017-03-12 17:33:46 +01:00
$invoked = false ;
$this -> start ( function ( Driver $loop ) use ( & $invoked ) {
2016-06-03 17:01:21 +02:00
$watcherId = $loop -> delay ( $msDelay = 10 , function () {
$this -> fail ( 'Watcher was not cancelled as expected' );
});
2017-03-12 17:33:46 +01:00
$loop -> defer ( function () use ( $loop , $watcherId , & $invoked ) {
2016-06-03 17:01:21 +02:00
$loop -> cancel ( $watcherId );
2017-03-12 17:33:46 +01:00
$invoked = true ;
2016-06-03 17:01:21 +02:00
});
2017-03-12 17:33:46 +01:00
2016-06-03 17:01:21 +02:00
$loop -> delay ( $msDelay = 5 , [ $loop , " stop " ]);
});
2017-03-12 17:33:46 +01:00
$this -> assertTrue ( $invoked );
2016-06-03 17:01:21 +02:00
}
2016-05-24 10:37:25 +02:00
2018-06-18 20:00:01 +02:00
public function testOnWritableWatcher ()
{
2016-06-03 17:01:21 +02:00
$flag = false ;
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) use ( & $flag ) {
2016-06-03 17:01:21 +02:00
$loop -> onWritable ( STDOUT , function () use ( $loop , & $flag ) {
$flag = true ;
$loop -> stop ();
});
$loop -> delay ( $msDelay = 5 , [ $loop , " stop " ]);
});
$this -> assertTrue ( $flag );
}
2016-05-24 10:37:25 +02:00
2018-06-18 20:00:01 +02:00
public function testInitiallyDisabledWriteWatcher ()
{
2016-06-03 17:01:21 +02:00
$increment = 0 ;
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) {
2016-06-03 17:01:21 +02:00
$watcherId = $loop -> onWritable ( STDOUT , function () use ( & $increment ) {
$increment ++ ;
});
$loop -> disable ( $watcherId );
$loop -> delay ( $msDelay = 5 , [ $loop , " stop " ]);
});
$this -> assertSame ( 0 , $increment );
}
2018-06-18 20:00:01 +02:00
public function testInitiallyDisabledWriteWatcherIsTriggeredOnceEnabled ()
{
2016-06-03 17:01:21 +02:00
$this -> expectOutputString ( " 12 " );
$this -> start ( function ( Driver $loop ) {
$watcherId = $loop -> onWritable ( STDOUT , function () use ( $loop ) {
echo 2 ;
$loop -> stop ();
});
$loop -> disable ( $watcherId );
$loop -> defer ( function () use ( $loop , $watcherId ) {
$loop -> enable ( $watcherId );
echo 1 ;
});
});
}
2016-06-07 01:53:28 +02:00
/** @expectedException \RuntimeException */
2018-06-18 20:00:01 +02:00
public function testStreamWatcherDoesntSwallowExceptions ()
{
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) {
2016-06-03 17:01:21 +02:00
$loop -> onWritable ( STDOUT , function () {
throw new \RuntimeException ;
});
$loop -> delay ( $msDelay = 5 , [ $loop , " stop " ]);
});
}
2018-06-18 20:00:01 +02:00
public function testReactorRunsUntilNoWatchersRemain ()
{
2016-06-03 17:01:21 +02:00
$var1 = $var2 = 0 ;
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) use ( & $var1 , & $var2 ) {
2016-06-03 17:01:21 +02:00
$loop -> repeat ( $msDelay = 1 , function ( $watcherId ) use ( $loop , & $var1 ) {
if ( ++ $var1 === 3 ) {
$loop -> cancel ( $watcherId );
}
});
$loop -> onWritable ( STDOUT , function ( $watcherId ) use ( $loop , & $var2 ) {
if ( ++ $var2 === 4 ) {
$loop -> cancel ( $watcherId );
}
});
});
$this -> assertSame ( 3 , $var1 );
$this -> assertSame ( 4 , $var2 );
}
2018-06-18 20:00:01 +02:00
public function testReactorRunsUntilNoWatchersRemainWhenStartedDeferred ()
{
2016-06-03 17:01:21 +02:00
$var1 = $var2 = 0 ;
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) use ( & $var1 , & $var2 ) {
$loop -> defer ( function () use ( $loop , & $var1 , & $var2 ) {
2016-06-03 17:01:21 +02:00
$loop -> repeat ( $msDelay = 1 , function ( $watcherId ) use ( $loop , & $var1 ) {
if ( ++ $var1 === 3 ) {
$loop -> cancel ( $watcherId );
}
});
$loop -> onWritable ( STDOUT , function ( $watcherId ) use ( $loop , & $var2 ) {
if ( ++ $var2 === 4 ) {
$loop -> cancel ( $watcherId );
}
});
});
});
$this -> assertSame ( 3 , $var1 );
$this -> assertSame ( 4 , $var2 );
}
2018-06-18 20:00:01 +02:00
public function testOptionalCallbackDataPassedOnInvocation ()
{
2016-06-03 17:01:21 +02:00
$callbackData = new \StdClass ;
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) use ( $callbackData ) {
2017-12-05 08:48:36 +01:00
$loop -> defer ( function ( $watcherId , $callbackData ) {
2016-06-03 17:01:21 +02:00
$callbackData -> defer = true ;
}, $callbackData );
2017-12-05 08:48:36 +01:00
$loop -> delay ( $msDelay = 1 , function ( $watcherId , $callbackData ) {
2016-06-03 17:01:21 +02:00
$callbackData -> delay = true ;
}, $callbackData );
$loop -> repeat ( $msDelay = 1 , function ( $watcherId , $callbackData ) use ( $loop ) {
$callbackData -> repeat = true ;
$loop -> cancel ( $watcherId );
}, $callbackData );
$loop -> onWritable ( STDERR , function ( $watcherId , $stream , $callbackData ) use ( $loop ) {
$callbackData -> onWritable = true ;
$loop -> cancel ( $watcherId );
}, $callbackData );
});
$this -> assertTrue ( $callbackData -> defer );
$this -> assertTrue ( $callbackData -> delay );
$this -> assertTrue ( $callbackData -> repeat );
$this -> assertTrue ( $callbackData -> onWritable );
}
2016-06-07 01:53:28 +02:00
2018-06-18 20:00:01 +02:00
public function testLoopStopPreventsTimerExecution ()
{
2017-11-29 13:36:50 +01:00
$t = \microtime ( 1 );
2017-03-10 21:31:57 +01:00
$this -> start ( function ( Driver $loop ) {
2016-12-23 01:43:09 +01:00
$loop -> delay ( $msDelay = 10000 , function () {
$this -> fail ( " Timer was executed despite stopped loop " );
});
$loop -> defer ( function () use ( $loop ) {
$loop -> stop ();
});
});
2018-11-12 21:04:12 +01:00
$this -> assertGreaterThan ( \microtime ( 1 ), $t + 0.1 );
2016-12-23 01:43:09 +01:00
}
2018-06-18 20:00:01 +02:00
public function testDeferEnabledInNextTick ()
{
2017-03-10 21:31:57 +01:00
$tick = function () {
$this -> loop -> defer ( function () {
2017-01-06 17:21:37 +01:00
$this -> loop -> stop ();
});
$this -> loop -> run ();
};
$invoked = 0 ;
2017-01-06 17:51:50 +01:00
$watcher = $this -> loop -> onWritable ( STDOUT , function () use ( & $invoked ) {
2017-01-06 17:21:37 +01:00
$invoked ++ ;
});
$tick ();
$tick ();
$tick ();
2017-01-06 17:51:50 +01:00
$this -> loop -> disable ( $watcher );
$this -> loop -> enable ( $watcher );
2017-01-06 17:21:37 +01:00
$tick (); // disable + immediate enable after a tick should have no effect either
2017-04-24 15:39:08 +02:00
$this -> assertSame ( 4 , $invoked );
2017-01-06 17:21:37 +01:00
}
2016-08-14 21:14:43 +02:00
// getState and setState are final, but test it here again to be sure
2018-06-18 20:00:01 +02:00
public function testRegistry ()
{
2016-08-14 21:14:43 +02:00
$this -> assertNull ( $this -> loop -> getState ( " foo " ));
$this -> loop -> setState ( " foo " , NAN );
2017-04-24 15:39:08 +02:00
$this -> assertNan ( $this -> loop -> getState ( " foo " ));
2016-08-14 21:14:43 +02:00
$this -> loop -> setState ( " foo " , " 1 " );
$this -> assertNull ( $this -> loop -> getState ( " bar " ));
$this -> loop -> setState ( " baz " , - INF );
2016-06-07 01:53:28 +02:00
// running must not affect state
$this -> loop -> defer ([ $this -> loop , " stop " ]);
$this -> loop -> run ();
2016-08-14 21:14:43 +02:00
$this -> assertSame ( - INF , $this -> loop -> getState ( " baz " ));
$this -> assertSame ( " 1 " , $this -> loop -> getState ( " foo " ));
2016-06-07 01:53:28 +02:00
}
/** @dataProvider provideRegistryValues */
2018-06-18 20:00:01 +02:00
public function testRegistryValues ( $val )
{
2016-08-14 21:14:43 +02:00
$this -> loop -> setState ( " foo " , $val );
$this -> assertSame ( $val , $this -> loop -> getState ( " foo " ));
2016-06-07 01:53:28 +02:00
}
2016-05-24 10:37:25 +02:00
2018-06-18 20:00:01 +02:00
public function provideRegistryValues ()
{
2016-06-07 01:53:28 +02:00
return [
[ " string " ],
[ 0 ],
[ PHP_INT_MIN ],
[ - 1.0 ],
[ true ],
[ false ],
[[]],
[ null ],
[ new \StdClass ],
];
2016-05-24 10:37:25 +02:00
}
2017-03-12 21:02:26 +01:00
2018-06-18 20:00:01 +02:00
public function testRethrowsFromCallbacks ()
{
2017-03-15 07:32:43 +01:00
foreach ([ " onReadable " , " onWritable " , " defer " , " delay " , " repeat " , " onSignal " ] as $watcher ) {
$promises = [
new Failure ( new \Exception ( " rethrow test " )),
new RejectedReactPromise ( new \Exception ( " rethrow test " )),
new Coroutine (( function () {
if ( false ) {
yield ;
}
2017-03-12 21:02:26 +01:00
2017-03-15 07:32:43 +01:00
throw new \Exception ( " rethrow test " );
})()),
( function () {
if ( false ) {
yield ;
}
2017-03-12 21:02:26 +01:00
2017-03-15 07:32:43 +01:00
throw new \Exception ( " rethrow test " );
})(),
null ,
];
2017-03-15 00:34:37 +01:00
2017-03-15 07:32:43 +01:00
foreach ( $promises as $promise ) {
2017-03-15 00:34:37 +01:00
if ( $watcher === " onSignal " ) {
$this -> checkForSignalCapability ();
}
try {
$args = [];
switch ( $watcher ) {
case " onSignal " :
$args [] = SIGUSR1 ;
break ;
case " onWritable " :
$args [] = STDOUT ;
break ;
case " onReadable " :
2020-07-14 21:45:35 +02:00
$ends = \stream_socket_pair (
\stripos ( PHP_OS , " win " ) === 0 ? STREAM_PF_INET : STREAM_PF_UNIX ,
STREAM_SOCK_STREAM ,
STREAM_IPPROTO_IP
);
2017-11-29 13:36:50 +01:00
\fwrite ( $ends [ 0 ], " trigger readability watcher " );
2017-03-15 00:34:37 +01:00
$args [] = $ends [ 1 ];
break ;
case " delay " :
case " repeat " :
$args [] = 5 ;
break ;
}
if ( $promise === null ) {
$args [] = function ( $watcherId ) {
$this -> loop -> cancel ( $watcherId );
throw new \Exception ( " rethrow test " );
};
} else {
$args [] = function ( $watcherId ) use ( $promise ) {
$this -> loop -> cancel ( $watcherId );
return $promise ;
};
}
2017-11-29 13:36:50 +01:00
\call_user_func_array ([ $this -> loop , $watcher ], $args );
2017-03-15 00:34:37 +01:00
if ( $watcher == " onSignal " ) {
$this -> loop -> delay ( 100 , function () {
\posix_kill ( \getmypid (), \SIGUSR1 );
});
}
$this -> loop -> run ();
$this -> fail ( " Didn't throw expected exception. " );
} catch ( \Exception $e ) {
$this -> assertSame ( " rethrow test " , $e -> getMessage ());
}
}
}
2017-03-12 21:02:26 +01:00
}
2017-04-19 18:16:37 +02:00
2018-06-18 20:00:01 +02:00
public function testMultipleWatchersOnSameDescriptor ()
{
2020-07-14 21:45:35 +02:00
$sockets = \stream_socket_pair (
\stripos ( PHP_OS , " win " ) === 0 ? STREAM_PF_INET : STREAM_PF_UNIX ,
STREAM_SOCK_STREAM ,
STREAM_IPPROTO_IP
);
2017-04-21 17:02:10 +02:00
\fwrite ( $sockets [ 1 ], " testing " );
$invoked = 0 ;
$watcher1 = $this -> loop -> onReadable ( $sockets [ 0 ], function ( $watcher ) use ( & $invoked ) {
$invoked += 1 ;
$this -> loop -> disable ( $watcher );
});
2017-12-05 08:48:36 +01:00
$watcher2 = $this -> loop -> onReadable ( $sockets [ 0 ], function ( $watcher ) use ( & $invoked ) {
2017-04-21 17:02:10 +02:00
$invoked += 10 ;
$this -> loop -> disable ( $watcher );
});
2017-04-21 17:08:58 +02:00
$watcher3 = $this -> loop -> onWritable ( $sockets [ 0 ], function ( $watcher ) use ( & $invoked ) {
2017-04-21 17:02:10 +02:00
$invoked += 100 ;
$this -> loop -> disable ( $watcher );
});
2017-12-05 08:48:36 +01:00
$watcher4 = $this -> loop -> onWritable ( $sockets [ 0 ], function ( $watcher ) use ( & $invoked ) {
2017-04-21 17:02:10 +02:00
$invoked += 1000 ;
$this -> loop -> disable ( $watcher );
2017-04-21 19:18:40 +02:00
});
2017-05-02 22:09:11 +02:00
$this -> loop -> defer ( function () use ( $watcher1 , $watcher3 ) {
2017-05-02 21:51:52 +02:00
$this -> loop -> delay ( 200 , function () use ( $watcher1 , $watcher3 ) {
$this -> loop -> enable ( $watcher1 );
$this -> loop -> enable ( $watcher3 );
});
2017-04-21 17:02:10 +02:00
});
$this -> loop -> run ();
$this -> assertSame ( 1212 , $invoked );
2017-04-21 19:18:40 +02:00
$this -> loop -> enable ( $watcher1 );
$this -> loop -> enable ( $watcher3 );
$this -> loop -> delay ( 100 , function () use ( $watcher2 , $watcher4 ) {
$this -> loop -> enable ( $watcher2 );
$this -> loop -> enable ( $watcher4 );
});
2017-04-21 17:02:10 +02:00
$this -> loop -> run ();
$this -> assertSame ( 2323 , $invoked );
}
2018-06-18 20:00:01 +02:00
public function testTimerIntervalCountedWhenNotRunning ()
{
2020-07-14 21:45:35 +02:00
$this -> loop -> delay ( 1000 , function () use ( & $start ) {
2017-04-20 16:59:46 +02:00
$this -> assertLessThan ( 0.5 , \microtime ( true ) - $start );
2017-04-19 18:16:37 +02:00
});
2020-07-14 21:45:35 +02:00
\usleep ( 600000 ); // 600ms instead of 500ms to allow for variations in timing.
$start = \microtime ( true );
2017-04-19 18:16:37 +02:00
$this -> loop -> run ();
}
2017-05-22 19:54:04 +02:00
2018-06-18 20:00:01 +02:00
public function testShortTimerDoesNotBlockOtherTimers ()
{
2017-05-22 19:54:04 +02:00
$this -> loop -> repeat ( 0 , function () {
static $i = 0 ;
if ( ++ $i === 5 ) {
$this -> fail ( " Loop continues with repeat watcher " );
}
\usleep ( 2000 );
});
$this -> loop -> delay ( 2 , function () {
$this -> assertTrue ( true );
$this -> loop -> stop ();
});
$this -> loop -> run ();
}
2017-05-23 19:46:23 +02:00
2018-06-18 20:00:01 +02:00
public function testTwoShortRepeatTimersWorkAsExpected ()
{
2017-05-23 19:46:23 +02:00
$this -> loop -> repeat ( 0 , function () use ( & $j ) {
static $i = 0 ;
if ( ++ $i === 5 ) {
$this -> loop -> stop ();
}
$j = $i ;
});
$this -> loop -> repeat ( 0 , function () use ( & $k ) {
static $i = 0 ;
if ( ++ $i === 5 ) {
$this -> loop -> stop ();
}
$k = $i ;
});
$this -> loop -> run ();
$this -> assertLessThan ( 2 , \abs ( $j - $k ));
$this -> assertNotSame ( 0 , $j );
}
2017-09-17 12:18:04 +02:00
2018-01-06 03:32:57 +01:00
public function testNow ()
{
$now = $this -> loop -> now ();
$this -> loop -> delay ( 100 , function () use ( $now ) {
$now += 100 ;
$new = $this -> loop -> now ();
// Allow a few milliseconds of inaccuracy.
2018-12-11 11:31:37 +01:00
$this -> assertGreaterThanOrEqual ( $now - 1 , $new );
$this -> assertLessThanOrEqual ( $now + 10 , $new );
2018-01-06 03:32:57 +01:00
});
$this -> loop -> run ();
}
2018-06-18 20:00:01 +02:00
public function testBug163ConsecutiveDelayed ()
{
2017-09-17 12:18:04 +02:00
$emits = 3 ;
2017-09-19 19:58:07 +02:00
$this -> loop -> defer ( function () use ( & $time , $emits ) {
2017-11-29 13:36:50 +01:00
$time = \microtime ( true );
2017-09-17 12:18:04 +02:00
for ( $i = 0 ; $i < $emits ; ++ $i ) {
yield new Delayed ( 100 );
}
2017-11-29 13:36:50 +01:00
$time = \microtime ( true ) - $time ;
2017-09-17 12:18:04 +02:00
});
2017-09-19 19:58:07 +02:00
$this -> loop -> run ();
2017-09-17 12:18:04 +02:00
$this -> assertGreaterThan ( 100 * $emits - 1 /* 1ms grace period */ , $time * 1000 );
}
2016-05-24 10:37:25 +02:00
}