getReactor(); $reactor->run(function($reactor) { $reactor->run(); }); } public function testImmediatelyWatcherRegistrationAndCancellation() { $reactor = $this->getReactor(); $watcherId = $reactor->immediately(function () {}); $this->assertInternalType("string", $watcherId); $info = $reactor->__debugInfo(); $this->assertSame(1, $info["immediates"]); // invoke enable() on active watcher to ensure it has no side-effects $reactor->enable($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(1, $info["immediates"]); // invoke disable() twice to ensure it has no side-effects $reactor->disable($watcherId); $reactor->disable($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(0, $info["immediates"]); $this->assertSame(1, $info["disabled"]); $reactor->cancel($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(0, $info["immediates"]); $watcherId = $reactor->immediately(function () {}); $info = $reactor->__debugInfo(); $this->assertSame(1, $info["immediates"]); $reactor->disable($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(0, $info["immediates"]); $this->assertSame(1, $info["disabled"]); $reactor->enable($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(1, $info["immediates"]); $this->assertSame(0, $info["disabled"]); $reactor->cancel($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(0, $info["immediates"]); // invoke cancel() again to ensure it has no side-effects $reactor->cancel($watcherId); } public function testOnceWatcherRegistrationAndCancellation() { $reactor = $this->getReactor(); $watcherId = $reactor->once(function () {}, 1000); $this->assertInternalType("string", $watcherId); $info = $reactor->__debugInfo(); $this->assertSame(1, $info["timers"]); // invoke enable() on active watcher to ensure it has no side-effects $reactor->enable($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(1, $info["timers"]); // invoke disable() twice to ensure it has no side-effects $reactor->disable($watcherId); $reactor->disable($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(0, $info["timers"]); $this->assertSame(1, $info["disabled"]); $reactor->cancel($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(0, $info["timers"]); $watcherId = $reactor->once(function () {}, 1000); $info = $reactor->__debugInfo(); $this->assertSame(1, $info["timers"]); $reactor->disable($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(0, $info["timers"]); $this->assertSame(1, $info["disabled"]); $reactor->enable($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(1, $info["timers"]); $this->assertSame(0, $info["disabled"]); $reactor->cancel($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(0, $info["timers"]); // invoke cancel() again to ensure it has no side-effects $reactor->cancel($watcherId); } public function testRepeatWatcherRegistrationAndCancellation() { $reactor = $this->getReactor(); $watcherId = $reactor->repeat(function () {}, 1000); $this->assertInternalType("string", $watcherId); $info = $reactor->__debugInfo(); $this->assertSame(1, $info["timers"]); // invoke enable() on active watcher to ensure it has no side-effects $reactor->enable($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(1, $info["timers"]); // invoke disable() twice to ensure it has no side-effects $reactor->disable($watcherId); $reactor->disable($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(0, $info["timers"]); $this->assertSame(1, $info["disabled"]); $reactor->cancel($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(0, $info["timers"]); $watcherId = $reactor->repeat(function () {}, 1000); $info = $reactor->__debugInfo(); $this->assertSame(1, $info["timers"]); $reactor->disable($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(0, $info["timers"]); $this->assertSame(1, $info["disabled"]); $reactor->enable($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(1, $info["timers"]); $this->assertSame(0, $info["disabled"]); $reactor->cancel($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(0, $info["timers"]); // invoke cancel() again to ensure it has no side-effects $reactor->cancel($watcherId); } public function testOnWritableWatcherRegistrationAndCancellation() { $reactor = $this->getReactor(); $watcherId = $reactor->onWritable(STDIN, function () {}); $this->assertInternalType("string", $watcherId); $info = $reactor->__debugInfo(); $this->assertSame(1, $info["io_writers"]); // invoke enable() on active watcher to ensure it has no side-effects $reactor->enable($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(1, $info["io_writers"]); // invoke disable() twice to ensure it has no side-effects $reactor->disable($watcherId); $reactor->disable($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(0, $info["io_writers"]); $this->assertSame(1, $info["disabled"]); $reactor->cancel($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(0, $info["io_writers"]); $watcherId = $reactor->onWritable(STDIN, function () {}); $info = $reactor->__debugInfo(); $this->assertSame(1, $info["io_writers"]); $reactor->disable($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(0, $info["io_writers"]); $this->assertSame(1, $info["disabled"]); $reactor->enable($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(1, $info["io_writers"]); $this->assertSame(0, $info["disabled"]); $reactor->cancel($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(0, $info["io_writers"]); // invoke cancel() again to ensure it has no side-effects $reactor->cancel($watcherId); } public function testOnReadableWatcherRegistrationAndCancellation() { $reactor = $this->getReactor(); $watcherId = $reactor->onReadable(STDIN, function () {}); $this->assertInternalType("string", $watcherId); $info = $reactor->__debugInfo(); $this->assertSame(1, $info["io_readers"]); // invoke enable() on active watcher to ensure it has no side-effects $reactor->enable($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(1, $info["io_readers"]); // invoke disable() twice to ensure it has no side-effects $reactor->disable($watcherId); $reactor->disable($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(0, $info["io_readers"]); $this->assertSame(1, $info["disabled"]); $reactor->cancel($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(0, $info["io_readers"]); $watcherId = $reactor->onReadable(STDIN, function () {}); $info = $reactor->__debugInfo(); $this->assertSame(1, $info["io_readers"]); $reactor->disable($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(0, $info["io_readers"]); $this->assertSame(1, $info["disabled"]); $reactor->enable($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(1, $info["io_readers"]); $this->assertSame(0, $info["disabled"]); $reactor->cancel($watcherId); $info = $reactor->__debugInfo(); $this->assertSame(0, $info["io_readers"]); // invoke cancel() again to ensure it has no side-effects $reactor->cancel($watcherId); } public function testEnableHasNoEffectOnNonexistentWatcher() { $reactor = $this->getReactor(); $reactor->enable("nonexistentWatcher"); } public function testDisableHasNoEffectOnNonexistentWatcher() { $reactor = $this->getReactor(); $reactor->disable("nonexistentWatcher"); } public function testCancelHasNoEffectOnNonexistentWatcher() { $reactor = $this->getReactor(); $reactor->cancel("nonexistentWatcher"); } /** * @expectedException \Exception * @expectedExceptionMessage coroutine error */ public function testImmediateCoroutineResolutionError() { $reactor = $this->getReactor(); $reactor->run(function($reactor) { yield; yield new \Amp\Pause(10, $reactor); throw new \Exception("coroutine error"); }); } public function testOnErrorCapturesUncaughtException() { $reactor = $this->getReactor(); $msg = ""; $reactor->onError(function ($error) use (&$msg, $reactor) { $msg = $error->getMessage(); }); $reactor->run(function($reactor) { throw new \Exception("coroutine error"); }); $this->assertSame("coroutine error", $msg); } /** * @expectedException \Exception * @expectedExceptionMessage errorception */ public function testOnErrorFailure() { $reactor = $this->getReactor(); $reactor->onError(function () { throw new \Exception("errorception"); }); $reactor->run(function($reactor) { yield; yield new \Amp\Pause(10, $reactor); throw new \Exception("coroutine error"); }); } public function testEnablingWatcherAllowsSubsequentInvocation() { $reactor = $this->getReactor(); $increment = 0; $watcherId = $reactor->immediately(function() use (&$increment) { $increment++; }); $reactor->disable($watcherId); $reactor->once([$reactor, "stop"], $msDelay = 50); $reactor->run(); $this->assertEquals(0, $increment); $reactor->enable($watcherId); $reactor->once([$reactor, "stop"], $msDelay = 50); $reactor->run(); $this->assertEquals(1, $increment); } public function testTimerWatcherParameterOrder() { $reactor = $this->getReactor(); $counter = 0; $reactor->immediately(function($reactorArg, $watcherId) use ($reactor, &$counter) { $this->assertSame($reactor, $reactorArg); if (++$counter === 3) { $reactor->stop(); } }); $reactor->once(function($reactorArg, $watcherId) use ($reactor, &$counter) { $this->assertSame($reactor, $reactorArg); if (++$counter === 3) { $reactor->stop(); } }, $msDelay = 1); $reactor->repeat(function($reactorArg, $watcherId) use ($reactor, &$counter) { $this->assertSame($reactor, $reactorArg); $reactor->cancel($watcherId); if (++$counter === 3) { $reactor->stop(); } }, $msDelay = 1); $reactor->run(); } public function testStreamWatcherParameterOrder() { $reactor = $this->getReactor(); $reactor->onWritable(STDOUT, function($reactorArg, $watcherId) use ($reactor) { $this->assertSame($reactor, $reactorArg); $this->assertTrue(is_string($watcherId)); $reactor->stop(); }); } public function testDisablingWatcherPreventsSubsequentInvocation() { $reactor = $this->getReactor(); $increment = 0; $watcherId = $reactor->immediately(function () use (&$increment) { $increment++; }); $reactor->disable($watcherId); $reactor->once([$reactor, "stop"], $msDelay = 50); $reactor->run(); $this->assertEquals(0, $increment); } public function testUnresolvedEventsAreReenabledOnRunFollowingPreviousStop() { $reactor = $this->getReactor(); $increment = 0; $reactor->once(function($reactor) use (&$increment) { $increment++; $reactor->stop(); }, $msDelay = 200); $reactor->run(function($reactor) { $reactor->stop(); }); $this->assertEquals(0, $increment); usleep(150000); $reactor->run(); $this->assertEquals(1, $increment); } public function testImmediateExecution() { $reactor = $this->getReactor(); $increment = 0; $reactor->immediately(function() use (&$increment) { $increment++; }); $reactor->tick(); $this->assertEquals(1, $increment); } public function testImmediatelyCallbacksDontRecurseInSameTick() { $reactor = $this->getReactor(); $increment = 0; $reactor->immediately(function() use (&$increment, $reactor) { $increment++; $reactor->immediately(function() use (&$increment) { $increment++; }); }); $reactor->tick(); $this->assertEquals(1, $increment); } public function testTickExecutesReadyEvents() { $reactor = $this->getReactor(); $increment = 0; $reactor->immediately(function() use (&$increment) { $increment++; }); $reactor->tick(); $this->assertEquals(1, $increment); } public function testRunExecutesEventsUntilExplicitlyStopped() { $reactor = $this->getReactor(); $increment = 0; $reactor->repeat(function($reactor, $watcherId) use (&$increment) { $increment++; if ($increment === 10) { $reactor->cancel($watcherId); } }, $msInterval = 5); $reactor->run(); $this->assertEquals(10, $increment); } public function testOnceReturnsEventWatcher() { $reactor = $this->getReactor(); $firstWatcherId = 'a'; $watcherId = $reactor->once(function(){}, $delay = 0); $this->assertSame($firstWatcherId, $watcherId); $watcherId = $reactor->immediately(function(){}); $this->assertSame(++$firstWatcherId, $watcherId); } /** * @expectedException RuntimeException * @expectedExceptionMessage test */ public function testReactorAllowsExceptionToBubbleUpDuringTick() { $reactor = $this->getReactor(); $reactor->immediately(function(){ throw new \RuntimeException('test'); }); $reactor->tick(); } /** * @expectedException RuntimeException * @expectedExceptionMessage test */ public function testReactorAllowsExceptionToBubbleUpDuringRun() { $reactor = $this->getReactor(); $reactor->immediately(function(){ throw new \RuntimeException('test'); }); $reactor->run(); } /** * @expectedException RuntimeException * @expectedExceptionMessage test */ public function testReactorAllowsExceptionToBubbleUpFromRepeatingAlarmDuringRun() { $reactor = $this->getReactor(); $reactor->repeat(function(){ throw new \RuntimeException('test'); }, $msInterval = 0); $reactor->run(); } public function testRepeatReturnsEventWatcher() { $reactor = $this->getReactor(); $firstWatcherId = 'a'; $watcherId = $reactor->repeat(function(){}, $msInterval = 1000); $this->assertSame($firstWatcherId, $watcherId); $watcherId = $reactor->repeat(function(){}, $msInterval = 1000); $this->assertSame(++$firstWatcherId, $watcherId); } public function testCancelRemovesWatcher() { $reactor = $this->getReactor(); $watcherId = $reactor->once(function(){ $this->fail('Watcher was not cancelled as expected'); }, $msDelay = 20); $reactor->immediately(function() use ($reactor, $watcherId) { $reactor->cancel($watcherId); }); $reactor->once(function() use ($reactor) { $reactor->stop(); }, $msDelay = 5); $reactor->run(); } public function testOnWritableWatcher() { $reactor = $this->getReactor(); $flag = FALSE; $reactor->onWritable(STDOUT, function() use ($reactor, &$flag) { $flag = TRUE; $reactor->stop(); }); $reactor->once([$reactor, "stop"], $msDelay = 50); $reactor->run(); $this->assertTrue($flag); } public function testInitiallyDisabledWriteWatcher() { $reactor = $this->getReactor(); $increment = 0; $options = ["enable" => false]; $reactor->onWritable(STDOUT, function() use (&$increment) { $increment++; }, $options); $reactor->once([$reactor, "stop"], $msDelay = 50); $reactor->run(); $this->assertSame(0, $increment); } public function testInitiallyDisabledWriteWatcherIsTriggeredOnceEnabled() { $reactor = $this->getReactor(); $increment = 0; $options = ["enable" => false]; $watcherId = $reactor->onWritable(STDOUT, function() use (&$increment) { $increment++; }, $options); $reactor->immediately(function() use ($reactor, $watcherId) { $reactor->enable($watcherId); }); $reactor->once([$reactor, "stop"], $msDelay = 250); $reactor->run(); $this->assertTrue($increment > 0); } /** * @expectedException RuntimeException */ public function testStreamWatcherDoesntSwallowExceptions() { $reactor = $this->getReactor(); $reactor->onWritable(STDOUT, function() { throw new \RuntimeException; }); $reactor->once([$reactor, "stop"], $msDelay = 50); $reactor->run(); } public function testGarbageCollection() { $reactor = $this->getReactor(); $reactor->once([$reactor, "stop"], $msDelay = 100); $reactor->run(); } public function testOnStartGeneratorResolvesAutomatically() { $test = ''; $this->getReactor()->run(function($reactor) use (&$test) { yield; $test = "Thus Spake Zarathustra"; $reactor->once(function() use ($reactor) { $reactor->stop(); }, 1); }); $this->assertSame("Thus Spake Zarathustra", $test); } public function testImmediatelyGeneratorResolvesAutomatically() { $reactor = $this->getReactor(); $test = ''; $reactor->immediately(function($reactor) use (&$test) { yield; $test = "The abyss will gaze back into you"; $reactor->once(function($reactor) { $reactor->stop(); }, 50); }); $reactor->run(); $this->assertSame("The abyss will gaze back into you", $test); } public function testOnceGeneratorResolvesAutomatically() { $reactor = $this->getReactor(); $test = ''; $gen = function($reactor) use (&$test) { yield; $test = "There are no facts, only interpretations."; $reactor->once(function() use ($reactor) { $reactor->stop(); }, 50); }; $reactor->once($gen, 1); $reactor->run(); $this->assertSame("There are no facts, only interpretations.", $test); } public function testRepeatGeneratorResolvesAutomatically() { $reactor = $this->getReactor(); $test = ''; $gen = function($reactor, $watcherId) use (&$test) { $reactor->cancel($watcherId); yield; $test = "Art is the supreme task"; $reactor->stop(); }; $reactor->repeat($gen, 50); $reactor->run(); $this->assertSame("Art is the supreme task", $test); } public function testOnErrorCallbackInterceptsUncaughtException() { $var = null; $reactor = $this->getReactor(); $reactor->onError(function($e) use (&$var) { $var = $e->getMessage(); }); $reactor->run(function() { throw new \Exception('test'); }); $this->assertSame('test', $var); } public function testReactorRunsUntilNoWatchersRemain() { $reactor = $this->getReactor(); $var1 = 0; $reactor->repeat(function($reactor, $watcherId) use (&$var1) { if (++$var1 === 3) { $reactor->cancel($watcherId); } }, 0); $var2 = 0; $reactor->onWritable(STDOUT, function($reactor, $watcherId) use (&$var2) { if (++$var2 === 4) { $reactor->cancel($watcherId); } }); $reactor->run(); $this->assertSame(3, $var1); $this->assertSame(4, $var2); } public function testReactorRunsUntilNoWatchersRemainWhenStartedImmediately() { $reactor = $this->getReactor(); $var1 = 0; $var2 = 0; $reactor->run(function($reactor) use (&$var1, &$var2) { $reactor->repeat(function($reactor, $watcherId) use (&$var1) { if (++$var1 === 3) { $reactor->cancel($watcherId); } }, 0); $reactor->onWritable(STDOUT, function($reactor, $watcherId) use (&$var2) { if (++$var2 === 4) { $reactor->cancel($watcherId); } }); }); $this->assertSame(3, $var1); $this->assertSame(4, $var2); } public function testOptionalCallbackDataPassedOnInvocation() { $callbackData = new \StdClass; $options = ["cb_data" => $callbackData]; $reactor = $this->getReactor(); $reactor->immediately(function($reactor, $watcherId, $callbackData) { $callbackData->immediately = true; }, $options); $reactor->once(function($reactor, $watcherId, $callbackData) { $callbackData->once = true; }, 1, $options); $reactor->repeat(function($reactor, $watcherId, $callbackData) { $callbackData->repeat = true; $reactor->cancel($watcherId); }, 1, $options); $reactor->onWritable(STDERR, function($reactor, $watcherId, $stream, $callbackData) { $callbackData->onWritable = true; $reactor->cancel($watcherId); }, $options); $reactor->run(); $this->assertTrue($callbackData->immediately); $this->assertTrue($callbackData->once); $this->assertTrue($callbackData->repeat); $this->assertTrue($callbackData->onWritable); } public function testOptionalRepeatWatcherDelay() { $reactor = $this->getReactor(); $invoked = false; $reactor->repeat(function($reactor, $watcherId) use (&$invoked) { $invoked = true; $reactor->cancel($watcherId); }, $msInterval = 10000, $options = ["ms_delay" => 1]); $reactor->once([$reactor, "stop"], 50); $reactor->run(); $this->assertTrue($invoked); } public function testOptionalDisable() { $reactor = $this->getReactor(); $options = ["enable" => false]; $reactor->immediately(function($reactor, $watcherId, $callbackData) { $this->fail("disabled watcher should not invoke callback"); }, $options); $reactor->once(function($reactor, $watcherId, $callbackData) { $this->fail("disabled watcher should not invoke callback"); }, 1, $options); $reactor->repeat(function($reactor, $watcherId, $callbackData) { $this->fail("disabled watcher should not invoke callback"); $reactor->cancel($watcherId); }, 1, $options); $reactor->onWritable(STDERR, function($reactor, $watcherId, $stream, $callbackData) { $this->fail("disabled watcher should not invoke callback"); $reactor->cancel($watcherId); }, $options); $reactor->run(); } }