2014-03-07 18:02:03 +01:00
|
|
|
<?php
|
|
|
|
|
2014-09-23 04:38:32 +02:00
|
|
|
namespace Amp\Test;
|
2014-03-07 18:02:03 +01:00
|
|
|
|
|
|
|
abstract class ReactorTest extends \PHPUnit_Framework_TestCase {
|
|
|
|
abstract protected function getReactor();
|
|
|
|
|
|
|
|
public function testEnablingWatcherAllowsSubsequentInvocation() {
|
|
|
|
$reactor = $this->getReactor();
|
|
|
|
$increment = 0;
|
|
|
|
|
|
|
|
$watcherId = $reactor->immediately(function() use (&$increment) { $increment++; });
|
|
|
|
$reactor->disable($watcherId);
|
|
|
|
|
2015-04-03 17:56:16 +02:00
|
|
|
$reactor->once([$reactor, "stop"], $msDelay = 50);
|
2014-03-07 18:02:03 +01:00
|
|
|
|
|
|
|
$reactor->run();
|
|
|
|
$this->assertEquals(0, $increment);
|
|
|
|
|
|
|
|
$reactor->enable($watcherId);
|
2015-04-03 17:56:16 +02:00
|
|
|
$reactor->once([$reactor, "stop"], $msDelay = 50);
|
2014-03-07 18:02:03 +01:00
|
|
|
|
|
|
|
$reactor->run();
|
|
|
|
|
|
|
|
$this->assertEquals(1, $increment);
|
|
|
|
}
|
|
|
|
|
2014-08-09 16:25:07 +02:00
|
|
|
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);
|
2014-08-21 19:54:02 +02:00
|
|
|
$this->assertTrue(is_string($watcherId));
|
2014-08-09 16:25:07 +02:00
|
|
|
$reactor->stop();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-03-07 18:02:03 +01:00
|
|
|
public function testDisablingWatcherPreventsSubsequentInvocation() {
|
|
|
|
$reactor = $this->getReactor();
|
|
|
|
$increment = 0;
|
|
|
|
|
|
|
|
$watcherId = $reactor->immediately(function() use (&$increment) {
|
|
|
|
$increment++;
|
|
|
|
});
|
|
|
|
|
|
|
|
$reactor->disable($watcherId);
|
2015-04-03 17:56:16 +02:00
|
|
|
$reactor->once([$reactor, "stop"], $msDelay = 50);
|
2014-03-07 18:02:03 +01:00
|
|
|
$reactor->run();
|
|
|
|
|
|
|
|
$this->assertEquals(0, $increment);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testUnresolvedEventsAreReenabledOnRunFollowingPreviousStop() {
|
|
|
|
$reactor = $this->getReactor();
|
|
|
|
$increment = 0;
|
2014-12-02 07:09:51 +01:00
|
|
|
$reactor->once(function($reactor) use (&$increment) {
|
2014-03-07 18:02:03 +01:00
|
|
|
$increment++;
|
|
|
|
$reactor->stop();
|
2014-12-02 07:09:51 +01:00
|
|
|
}, $msDelay = 200);
|
|
|
|
|
|
|
|
$reactor->run(function($reactor) {
|
|
|
|
$reactor->stop();
|
|
|
|
});
|
2014-03-07 18:02:03 +01:00
|
|
|
|
|
|
|
$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);
|
|
|
|
}
|
|
|
|
|
2014-08-08 20:23:00 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2014-03-07 18:02:03 +01:00
|
|
|
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;
|
2015-05-25 18:47:04 +02:00
|
|
|
$reactor->repeat(function($reactor, $watcherId) use (&$increment) {
|
|
|
|
$increment++;
|
|
|
|
if ($increment === 10) {
|
|
|
|
$reactor->cancel($watcherId);
|
2014-03-07 18:02:03 +01:00
|
|
|
}
|
2015-05-25 18:47:04 +02:00
|
|
|
}, $msInterval = 5);
|
2014-03-07 18:02:03 +01:00
|
|
|
|
|
|
|
$reactor->run();
|
|
|
|
|
|
|
|
$this->assertEquals(10, $increment);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testOnceReturnsEventWatcher() {
|
|
|
|
$reactor = $this->getReactor();
|
|
|
|
|
2015-03-23 16:07:40 +01:00
|
|
|
$firstWatcherId = 'a';
|
2014-03-07 18:02:03 +01:00
|
|
|
$watcherId = $reactor->once(function(){}, $delay = 0);
|
|
|
|
$this->assertSame($firstWatcherId, $watcherId);
|
|
|
|
|
|
|
|
$watcherId = $reactor->immediately(function(){});
|
2015-03-23 16:07:40 +01:00
|
|
|
$this->assertSame(++$firstWatcherId, $watcherId);
|
2014-03-07 18:02:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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();
|
|
|
|
|
2015-03-23 16:07:40 +01:00
|
|
|
$firstWatcherId = 'a';
|
2014-03-07 18:02:03 +01:00
|
|
|
$watcherId = $reactor->repeat(function(){}, $msInterval = 1000);
|
|
|
|
$this->assertSame($firstWatcherId, $watcherId);
|
2014-06-11 18:21:46 +02:00
|
|
|
|
|
|
|
$watcherId = $reactor->repeat(function(){}, $msInterval = 1000);
|
2015-03-23 16:07:40 +01:00
|
|
|
$this->assertSame(++$firstWatcherId, $watcherId);
|
2014-03-07 18:02:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
});
|
2015-04-03 17:56:16 +02:00
|
|
|
$reactor->once([$reactor, "stop"], $msDelay = 50);
|
2014-03-07 18:02:03 +01:00
|
|
|
|
|
|
|
$reactor->run();
|
|
|
|
$this->assertTrue($flag);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testInitiallyDisabledWriteWatcher() {
|
|
|
|
$reactor = $this->getReactor();
|
|
|
|
|
|
|
|
$increment = 0;
|
2015-04-03 17:56:16 +02:00
|
|
|
$options = ["enable" => false];
|
|
|
|
$reactor->onWritable(STDOUT, function() use (&$increment) { $increment++; }, $options);
|
|
|
|
$reactor->once([$reactor, "stop"], $msDelay = 50);
|
2014-03-07 18:02:03 +01:00
|
|
|
$reactor->run();
|
|
|
|
|
|
|
|
$this->assertSame(0, $increment);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testInitiallyDisabledWriteWatcherIsTriggeredOnceEnabled() {
|
|
|
|
$reactor = $this->getReactor();
|
|
|
|
|
|
|
|
$increment = 0;
|
2015-04-03 17:56:16 +02:00
|
|
|
$options = ["enable" => false];
|
|
|
|
$watcherId = $reactor->onWritable(STDOUT, function() use (&$increment) { $increment++; }, $options);
|
2014-11-11 22:07:15 +01:00
|
|
|
$reactor->immediately(function() use ($reactor, $watcherId) {
|
2014-03-07 18:02:03 +01:00
|
|
|
$reactor->enable($watcherId);
|
2014-11-11 22:07:15 +01:00
|
|
|
});
|
2014-03-07 18:02:03 +01:00
|
|
|
|
2015-04-03 17:56:16 +02:00
|
|
|
$reactor->once([$reactor, "stop"], $msDelay = 250);
|
2014-03-07 18:02:03 +01:00
|
|
|
$reactor->run();
|
|
|
|
|
|
|
|
$this->assertTrue($increment > 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @expectedException RuntimeException
|
|
|
|
*/
|
|
|
|
public function testStreamWatcherDoesntSwallowExceptions() {
|
|
|
|
$reactor = $this->getReactor();
|
|
|
|
$reactor->onWritable(STDOUT, function() { throw new \RuntimeException; });
|
2015-04-03 17:56:16 +02:00
|
|
|
$reactor->once([$reactor, "stop"], $msDelay = 50);
|
2014-03-07 18:02:03 +01:00
|
|
|
$reactor->run();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testGarbageCollection() {
|
|
|
|
$reactor = $this->getReactor();
|
2015-04-03 17:56:16 +02:00
|
|
|
$reactor->once([$reactor, "stop"], $msDelay = 100);
|
2014-03-07 18:02:03 +01:00
|
|
|
$reactor->run();
|
|
|
|
}
|
2014-09-22 22:47:48 +02:00
|
|
|
|
|
|
|
public function testOnStartGeneratorResolvesAutomatically() {
|
|
|
|
$test = '';
|
2015-02-03 06:21:07 +01:00
|
|
|
$this->getReactor()->run(function($reactor) use (&$test) {
|
2015-03-16 20:00:10 +01:00
|
|
|
yield;
|
2014-09-22 22:47:48 +02:00
|
|
|
$test = "Thus Spake Zarathustra";
|
2015-04-03 17:56:16 +02:00
|
|
|
$reactor->once(function() use ($reactor) { $reactor->stop(); }, 1);
|
2015-02-03 06:21:07 +01:00
|
|
|
});
|
2014-09-22 22:47:48 +02:00
|
|
|
$this->assertSame("Thus Spake Zarathustra", $test);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testImmediatelyGeneratorResolvesAutomatically() {
|
|
|
|
$reactor = $this->getReactor();
|
|
|
|
$test = '';
|
2015-04-03 17:56:16 +02:00
|
|
|
$reactor->immediately(function($reactor) use (&$test) {
|
2015-03-16 20:00:10 +01:00
|
|
|
yield;
|
2014-09-22 22:47:48 +02:00
|
|
|
$test = "The abyss will gaze back into you";
|
2015-04-03 17:56:16 +02:00
|
|
|
$reactor->once(function($reactor) { $reactor->stop(); }, 50);
|
|
|
|
});
|
2014-09-22 22:47:48 +02:00
|
|
|
$reactor->run();
|
|
|
|
$this->assertSame("The abyss will gaze back into you", $test);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testOnceGeneratorResolvesAutomatically() {
|
|
|
|
$reactor = $this->getReactor();
|
|
|
|
$test = '';
|
|
|
|
$gen = function($reactor) use (&$test) {
|
2015-03-16 20:00:10 +01:00
|
|
|
yield;
|
2014-09-22 22:47:48 +02:00
|
|
|
$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);
|
2015-03-16 20:00:10 +01:00
|
|
|
yield;
|
2014-09-22 22:47:48 +02:00
|
|
|
$test = "Art is the supreme task";
|
|
|
|
$reactor->stop();
|
|
|
|
};
|
|
|
|
$reactor->repeat($gen, 50);
|
|
|
|
$reactor->run();
|
|
|
|
$this->assertSame("Art is the supreme task", $test);
|
|
|
|
}
|
2014-12-02 01:45:57 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2014-12-05 01:30:23 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2015-04-03 17:56:16 +02:00
|
|
|
|
|
|
|
public function testOptionalCallbackDataPassedOnInvocation() {
|
|
|
|
$callbackData = new \StdClass;
|
2015-05-20 21:21:40 +02:00
|
|
|
$options = ["cb_data" => $callbackData];
|
2015-04-03 17:56:16 +02:00
|
|
|
$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();
|
2015-05-25 19:33:06 +02:00
|
|
|
$invoked = false;
|
|
|
|
$reactor->repeat(function($reactor, $watcherId) use (&$invoked) {
|
|
|
|
$invoked = true;
|
2015-04-03 17:56:16 +02:00
|
|
|
$reactor->cancel($watcherId);
|
2015-04-30 19:41:14 +02:00
|
|
|
}, $msInterval = 10000, $options = ["ms_delay" => 1]);
|
2015-05-25 19:33:06 +02:00
|
|
|
$reactor->once([$reactor, "stop"], 50);
|
2015-04-03 17:56:16 +02:00
|
|
|
$reactor->run();
|
2015-05-25 19:33:06 +02:00
|
|
|
$this->assertTrue($invoked);
|
2015-04-03 17:56:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
2014-03-07 18:02:03 +01:00
|
|
|
}
|