diff --git a/.gitattributes b/.gitattributes
index 0925d33..eccc763 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -3,4 +3,5 @@
/.travis.yml export-ignore
/examples/ export-ignore
/phpunit.xml.dist export-ignore
+/phpunit.xml.legacy export-ignore
/tests/ export-ignore
diff --git a/.travis.yml b/.travis.yml
index cc9e05b..e8bf15b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -29,4 +29,5 @@ install:
- composer install
script:
- - vendor/bin/phpunit --coverage-text
+ - if [[ "$TRAVIS_PHP_VERSION" > "7.2" ]]; then vendor/bin/phpunit --coverage-text; fi
+ - if [[ "$TRAVIS_PHP_VERSION" < "7.3" ]]; then vendor/bin/phpunit --coverage-text -c phpunit.xml.legacy; fi
diff --git a/composer.json b/composer.json
index d5a7138..ebcf44b 100644
--- a/composer.json
+++ b/composer.json
@@ -21,7 +21,7 @@
},
"require-dev": {
"clue/block-react": "^1.1",
- "phpunit/phpunit": "^9.0 || ^5.7 || ^4.8.35"
+ "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35"
},
"autoload": {
"psr-4": { "Clue\\React\\Redis\\": "src/" }
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 5395aa6..e19a12c 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,14 +1,19 @@
-
+
+
./tests/
-
-
+
+
./src/
-
-
-
\ No newline at end of file
+
+
+
diff --git a/phpunit.xml.legacy b/phpunit.xml.legacy
new file mode 100644
index 0000000..8d93c4f
--- /dev/null
+++ b/phpunit.xml.legacy
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+ ./tests/
+
+
+
+
+ ./src/
+
+
+
diff --git a/tests/FactoryStreamingClientTest.php b/tests/FactoryStreamingClientTest.php
index afe63db..2c577a1 100644
--- a/tests/FactoryStreamingClientTest.php
+++ b/tests/FactoryStreamingClientTest.php
@@ -136,27 +136,45 @@ public function testWillWriteAuthCommandIfRedisUnixUriContainsUserInfo()
public function testWillResolveWhenAuthCommandReceivesOkResponseIfRedisUriContainsUserInfo()
{
- $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write'))->getMock();
+ $dataHandler = null;
+ $stream = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock();
$stream->expects($this->once())->method('write')->with("*2\r\n$4\r\nauth\r\n$5\r\nworld\r\n");
+ $stream->expects($this->exactly(2))->method('on')->withConsecutive(
+ array('data', $this->callback(function ($arg) use (&$dataHandler) {
+ $dataHandler = $arg;
+ return true;
+ })),
+ array('close', $this->anything())
+ );
$this->connector->expects($this->once())->method('connect')->willReturn(Promise\resolve($stream));
$promise = $this->factory->createClient('redis://:world@localhost');
- $stream->emit('data', array("+OK\r\n"));
+ $this->assertTrue(is_callable($dataHandler));
+ $dataHandler("+OK\r\n");
$promise->then($this->expectCallableOnceWith($this->isInstanceOf('Clue\React\Redis\Client')));
}
public function testWillRejectAndCloseAutomaticallyWhenAuthCommandReceivesErrorResponseIfRedisUriContainsUserInfo()
{
- $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write', 'close'))->getMock();
+ $dataHandler = null;
+ $stream = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock();
$stream->expects($this->once())->method('write')->with("*2\r\n$4\r\nauth\r\n$5\r\nworld\r\n");
$stream->expects($this->once())->method('close');
+ $stream->expects($this->exactly(2))->method('on')->withConsecutive(
+ array('data', $this->callback(function ($arg) use (&$dataHandler) {
+ $dataHandler = $arg;
+ return true;
+ })),
+ array('close', $this->anything())
+ );
$this->connector->expects($this->once())->method('connect')->willReturn(Promise\resolve($stream));
$promise = $this->factory->createClient('redis://:world@localhost');
- $stream->emit('data', array("-ERR invalid password\r\n"));
+ $this->assertTrue(is_callable($dataHandler));
+ $dataHandler("-ERR invalid password\r\n");
$promise->then(null, $this->expectCallableOnceWith(
$this->logicalAnd(
@@ -182,27 +200,45 @@ public function testWillWriteSelectCommandIfRedisUnixUriContainsDbQueryParameter
public function testWillResolveWhenSelectCommandReceivesOkResponseIfRedisUriContainsPath()
{
- $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write'))->getMock();
+ $dataHandler = null;
+ $stream = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock();
$stream->expects($this->once())->method('write')->with("*2\r\n$6\r\nselect\r\n$3\r\n123\r\n");
+ $stream->expects($this->exactly(2))->method('on')->withConsecutive(
+ array('data', $this->callback(function ($arg) use (&$dataHandler) {
+ $dataHandler = $arg;
+ return true;
+ })),
+ array('close', $this->anything())
+ );
$this->connector->expects($this->once())->method('connect')->willReturn(Promise\resolve($stream));
$promise = $this->factory->createClient('redis://localhost/123');
- $stream->emit('data', array("+OK\r\n"));
+ $this->assertTrue(is_callable($dataHandler));
+ $dataHandler("+OK\r\n");
$promise->then($this->expectCallableOnceWith($this->isInstanceOf('Clue\React\Redis\Client')));
}
public function testWillRejectAndCloseAutomaticallyWhenSelectCommandReceivesErrorResponseIfRedisUriContainsPath()
{
- $stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write', 'close'))->getMock();
+ $dataHandler = null;
+ $stream = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock();
$stream->expects($this->once())->method('write')->with("*2\r\n$6\r\nselect\r\n$3\r\n123\r\n");
$stream->expects($this->once())->method('close');
+ $stream->expects($this->exactly(2))->method('on')->withConsecutive(
+ array('data', $this->callback(function ($arg) use (&$dataHandler) {
+ $dataHandler = $arg;
+ return true;
+ })),
+ array('close', $this->anything())
+ );
$this->connector->expects($this->once())->method('connect')->willReturn(Promise\resolve($stream));
$promise = $this->factory->createClient('redis://localhost/123');
- $stream->emit('data', array("-ERR DB index is out of range\r\n"));
+ $this->assertTrue(is_callable($dataHandler));
+ $dataHandler("-ERR DB index is out of range\r\n");
$promise->then(null, $this->expectCallableOnceWith(
$this->logicalAnd(
diff --git a/tests/FunctionalTest.php b/tests/FunctionalTest.php
index 0f5549d..7e70b71 100644
--- a/tests/FunctionalTest.php
+++ b/tests/FunctionalTest.php
@@ -149,10 +149,11 @@ public function testPubSub()
$deferred = new Deferred();
$consumer->on('message', $this->expectCallableOnce());
$consumer->on('message', array($deferred, 'resolve'));
- $consumer->subscribe($channel)->then($this->expectCallableOnce());
-
- // producer sends a single message
- $producer->publish($channel, 'hello world')->then($this->expectCallableOnceWith(1));
+ $once = $this->expectCallableOnceWith(1);
+ $consumer->subscribe($channel)->then(function() use ($producer, $channel, $once){
+ // producer sends a single message
+ $producer->publish($channel, 'hello world')->then($once);
+ })->then($this->expectCallableOnce());
// expect "message" event to take no longer than 0.1s
Block\await($deferred->promise(), $this->loop, 0.1);
diff --git a/tests/LazyClientTest.php b/tests/LazyClientTest.php
index 0df029a..bf3ceb6 100644
--- a/tests/LazyClientTest.php
+++ b/tests/LazyClientTest.php
@@ -156,8 +156,15 @@ public function testPingAfterPreviousFactoryRejectsUnderlyingClientWillCreateNew
public function testPingAfterPreviousUnderlyingClientAlreadyClosedWillCreateNewUnderlyingConnection()
{
- $client = $this->getMockBuilder('Clue\React\Redis\StreamingClient')->disableOriginalConstructor()->setMethods(array('__call'))->getMock();
+ $closeHandler = null;
+ $client = $this->getMockBuilder('Clue\React\Redis\Client')->getMock();
$client->expects($this->once())->method('__call')->with('ping')->willReturn(\React\Promise\resolve('PONG'));
+ $client->expects($this->any())->method('on')->withConsecutive(
+ array('close', $this->callback(function ($arg) use (&$closeHandler) {
+ $closeHandler = $arg;
+ return true;
+ }))
+ );
$this->factory->expects($this->exactly(2))->method('createClient')->willReturnOnConsecutiveCalls(
\React\Promise\resolve($client),
@@ -165,7 +172,8 @@ public function testPingAfterPreviousUnderlyingClientAlreadyClosedWillCreateNewU
);
$this->client->ping();
- $client->emit('close');
+ $this->assertTrue(is_callable($closeHandler));
+ $closeHandler();
$this->client->ping();
}
@@ -183,7 +191,7 @@ public function testPingAfterCloseWillRejectWithoutCreatingUnderlyingConnection(
public function testPingAfterPingWillNotStartIdleTimerWhenFirstPingResolves()
{
$deferred = new Deferred();
- $client = $this->getMockBuilder('Clue\React\Redis\StreamingClient')->disableOriginalConstructor()->setMethods(array('__call'))->getMock();
+ $client = $this->getMockBuilder('Clue\React\Redis\Client')->getMock();
$client->expects($this->exactly(2))->method('__call')->willReturnOnConsecutiveCalls(
$deferred->promise(),
new Promise(function () { })
@@ -201,7 +209,7 @@ public function testPingAfterPingWillNotStartIdleTimerWhenFirstPingResolves()
public function testPingAfterPingWillStartAndCancelIdleTimerWhenSecondPingStartsAfterFirstResolves()
{
$deferred = new Deferred();
- $client = $this->getMockBuilder('Clue\React\Redis\StreamingClient')->disableOriginalConstructor()->setMethods(array('__call'))->getMock();
+ $client = $this->getMockBuilder('Clue\React\Redis\Client')->getMock();
$client->expects($this->exactly(2))->method('__call')->willReturnOnConsecutiveCalls(
$deferred->promise(),
new Promise(function () { })
@@ -220,7 +228,7 @@ public function testPingAfterPingWillStartAndCancelIdleTimerWhenSecondPingStarts
public function testPingFollowedByIdleTimerWillCloseUnderlyingConnectionWithoutCloseEvent()
{
- $client = $this->getMockBuilder('Clue\React\Redis\StreamingClient')->disableOriginalConstructor()->setMethods(array('__call', 'close'))->getMock();
+ $client = $this->getMockBuilder('Clue\React\Redis\Client')->getMock();
$client->expects($this->once())->method('__call')->willReturn(\React\Promise\resolve());
$client->expects($this->once())->method('close')->willReturn(\React\Promise\resolve());
@@ -298,7 +306,7 @@ public function testCloseAfterPingWillCloseUnderlyingClientConnectionWhenAlready
public function testCloseAfterPingWillCancelIdleTimerWhenPingIsAlreadyResolved()
{
$deferred = new Deferred();
- $client = $this->getMockBuilder('Clue\React\Redis\StreamingClient')->disableOriginalConstructor()->setMethods(array('__call', 'close'))->getMock();
+ $client = $this->getMockBuilder('Clue\React\Redis\Client')->getMock();
$client->expects($this->once())->method('__call')->willReturn($deferred->promise());
$client->expects($this->once())->method('close');
@@ -316,7 +324,7 @@ public function testCloseAfterPingWillCancelIdleTimerWhenPingIsAlreadyResolved()
public function testCloseAfterPingRejectsWillEmitClose()
{
$deferred = new Deferred();
- $client = $this->getMockBuilder('Clue\React\Redis\StreamingClient')->disableOriginalConstructor()->setMethods(array('__call', 'close'))->getMock();
+ $client = $this->getMockBuilder('Clue\React\Redis\Client')->getMock();
$client->expects($this->once())->method('__call')->willReturn($deferred->promise());
$client->expects($this->once())->method('close')->willReturnCallback(function () use ($client) {
$client->emit('close');
@@ -358,9 +366,15 @@ public function testEndAfterPingWillEndUnderlyingClient()
public function testEndAfterPingWillCloseClientWhenUnderlyingClientEmitsClose()
{
- $client = $this->getMockBuilder('Clue\React\Redis\StreamingClient')->disableOriginalConstructor()->setMethods(array('__call', 'end'))->getMock();
+ $closeHandler = null;
+ $client = $this->getMockBuilder('Clue\React\Redis\Client')->getMock();
$client->expects($this->once())->method('__call')->with('ping')->willReturn(\React\Promise\resolve('PONG'));
$client->expects($this->once())->method('end');
+ $client->expects($this->any())->method('on')->willReturnCallback(function ($event, $callback) use (&$closeHandler) {
+ if ($event === 'close') {
+ $closeHandler = $callback;
+ }
+ });
$deferred = new Deferred();
$this->factory->expects($this->once())->method('createClient')->willReturn($deferred->promise());
@@ -371,14 +385,15 @@ public function testEndAfterPingWillCloseClientWhenUnderlyingClientEmitsClose()
$this->client->on('close', $this->expectCallableOnce());
$this->client->end();
- $client->emit('close');
+ $this->assertTrue(is_callable($closeHandler));
+ $closeHandler();
}
public function testEmitsNoErrorEventWhenUnderlyingClientEmitsError()
{
$error = new \RuntimeException();
- $client = $this->getMockBuilder('Clue\React\Redis\StreamingClient')->disableOriginalConstructor()->setMethods(array('__call'))->getMock();
+ $client = $this->getMockBuilder('Clue\React\Redis\Client')->getMock();
$client->expects($this->once())->method('__call')->willReturn(\React\Promise\resolve());
$deferred = new Deferred();
@@ -393,7 +408,7 @@ public function testEmitsNoErrorEventWhenUnderlyingClientEmitsError()
public function testEmitsNoCloseEventWhenUnderlyingClientEmitsClose()
{
- $client = $this->getMockBuilder('Clue\React\Redis\StreamingClient')->disableOriginalConstructor()->setMethods(array('__call'))->getMock();
+ $client = $this->getMockBuilder('Clue\React\Redis\Client')->getMock();
$client->expects($this->once())->method('__call')->willReturn(\React\Promise\resolve());
$deferred = new Deferred();
@@ -408,9 +423,16 @@ public function testEmitsNoCloseEventWhenUnderlyingClientEmitsClose()
public function testEmitsNoCloseEventButWillCancelIdleTimerWhenUnderlyingConnectionEmitsCloseAfterPingIsAlreadyResolved()
{
+ $closeHandler = null;
+ $client = $this->getMockBuilder('Clue\React\Redis\Client')->getMock();
$deferred = new Deferred();
- $client = $this->getMockBuilder('Clue\React\Redis\StreamingClient')->disableOriginalConstructor()->setMethods(array('__call'))->getMock();
$client->expects($this->once())->method('__call')->willReturn($deferred->promise());
+ $client->expects($this->any())->method('on')->withConsecutive(
+ array('close', $this->callback(function ($arg) use (&$closeHandler) {
+ $closeHandler = $arg;
+ return true;
+ }))
+ );
$this->factory->expects($this->once())->method('createClient')->willReturn(\React\Promise\resolve($client));
@@ -423,13 +445,20 @@ public function testEmitsNoCloseEventButWillCancelIdleTimerWhenUnderlyingConnect
$this->client->ping();
$deferred->resolve();
- $client->emit('close');
+ $this->assertTrue(is_callable($closeHandler));
+ $closeHandler();
}
public function testEmitsMessageEventWhenUnderlyingClientEmitsMessageForPubSubChannel()
{
- $client = $this->getMockBuilder('Clue\React\Redis\StreamingClient')->disableOriginalConstructor()->setMethods(array('__call'))->getMock();
+ $messageHandler = null;
+ $client = $this->getMockBuilder('Clue\React\Redis\Client')->getMock();
$client->expects($this->once())->method('__call')->willReturn(\React\Promise\resolve());
+ $client->expects($this->any())->method('on')->willReturnCallback(function ($event, $callback) use (&$messageHandler) {
+ if ($event === 'message') {
+ $messageHandler = $callback;
+ }
+ });
$deferred = new Deferred();
$this->factory->expects($this->once())->method('createClient')->willReturn($deferred->promise());
@@ -438,51 +467,73 @@ public function testEmitsMessageEventWhenUnderlyingClientEmitsMessageForPubSubCh
$deferred->resolve($client);
$this->client->on('message', $this->expectCallableOnce());
- $client->emit('message', array('foo', 'bar'));
+ $this->assertTrue(is_callable($messageHandler));
+ $messageHandler('foo', 'bar');
}
public function testEmitsUnsubscribeAndPunsubscribeEventsWhenUnderlyingClientClosesWhileUsingPubSubChannel()
{
- $client = $this->getMockBuilder('Clue\React\Redis\StreamingClient')->disableOriginalConstructor()->setMethods(array('__call'))->getMock();
+ $allHandler = null;
+ $client = $this->getMockBuilder('Clue\React\Redis\Client')->getMock();
$client->expects($this->exactly(6))->method('__call')->willReturn(\React\Promise\resolve());
+ $client->expects($this->any())->method('on')->willReturnCallback(function ($event, $callback) use (&$allHandler) {
+ if (!isset($allHandler[$event])) {
+ $allHandler[$event] = $callback;
+ }
+ });
$this->factory->expects($this->once())->method('createClient')->willReturn(\React\Promise\resolve($client));
$this->client->subscribe('foo');
- $client->emit('subscribe', array('foo', 1));
+ $this->assertTrue(is_callable($allHandler['subscribe']));
+ $allHandler['subscribe']('foo', 1);
$this->client->subscribe('bar');
- $client->emit('subscribe', array('bar', 2));
+ $this->assertTrue(is_callable($allHandler['subscribe']));
+ $allHandler['subscribe']('bar', 2);
$this->client->unsubscribe('bar');
- $client->emit('unsubscribe', array('bar', 1));
+ $this->assertTrue(is_callable($allHandler['unsubscribe']));
+ $allHandler['unsubscribe']('bar', 1);
$this->client->psubscribe('foo*');
- $client->emit('psubscribe', array('foo*', 1));
+ $this->assertTrue(is_callable($allHandler['psubscribe']));
+ $allHandler['psubscribe']('foo*', 1);
$this->client->psubscribe('bar*');
- $client->emit('psubscribe', array('bar*', 2));
+ $this->assertTrue(is_callable($allHandler['psubscribe']));
+ $allHandler['psubscribe']('bar*', 2);
$this->client->punsubscribe('bar*');
- $client->emit('punsubscribe', array('bar*', 1));
+ $this->assertTrue(is_callable($allHandler['punsubscribe']));
+ $allHandler['punsubscribe']('bar*', 1);
$this->client->on('unsubscribe', $this->expectCallableOnce());
$this->client->on('punsubscribe', $this->expectCallableOnce());
- $client->emit('close');
+
+ $this->assertTrue(is_callable($allHandler['close']));
+ $allHandler['close']();
}
public function testSubscribeWillResolveWhenUnderlyingClientResolvesSubscribeAndNotStartIdleTimerWithIdleDueToSubscription()
{
+ $subscribeHandler = null;
$deferred = new Deferred();
- $client = $this->getMockBuilder('Clue\React\Redis\StreamingClient')->disableOriginalConstructor()->setMethods(array('__call'))->getMock();
+ $client = $this->getMockBuilder('Clue\React\Redis\Client')->getMock();
$client->expects($this->once())->method('__call')->with('subscribe')->willReturn($deferred->promise());
+ $client->expects($this->any())->method('on')->willReturnCallback(function ($event, $callback) use (&$subscribeHandler) {
+ if ($event === 'subscribe' && $subscribeHandler === null) {
+ $subscribeHandler = $callback;
+ }
+ });
$this->factory->expects($this->once())->method('createClient')->willReturn(\React\Promise\resolve($client));
$this->loop->expects($this->never())->method('addTimer');
$promise = $this->client->subscribe('foo');
- $client->emit('subscribe', array('foo', 1));
+ $this->assertTrue(is_callable($subscribeHandler));
+ $subscribeHandler('foo', 1);
$deferred->resolve(array('subscribe', 'foo', 1));
$promise->then($this->expectCallableOnceWith(array('subscribe', 'foo', 1)));
@@ -490,23 +541,46 @@ public function testSubscribeWillResolveWhenUnderlyingClientResolvesSubscribeAnd
public function testUnsubscribeAfterSubscribeWillResolveWhenUnderlyingClientResolvesUnsubscribeAndStartIdleTimerWhenSubscriptionStopped()
{
+ $subscribeHandler = null;
+ $unsubscribeHandler = null;
$deferredSubscribe = new Deferred();
$deferredUnsubscribe = new Deferred();
- $client = $this->getMockBuilder('Clue\React\Redis\StreamingClient')->disableOriginalConstructor()->setMethods(array('__call'))->getMock();
+ $client = $this->getMockBuilder('Clue\React\Redis\Client')->getMock();
$client->expects($this->exactly(2))->method('__call')->willReturnOnConsecutiveCalls($deferredSubscribe->promise(), $deferredUnsubscribe->promise());
+ $client->expects($this->any())->method('on')->willReturnCallback(function ($event, $callback) use (&$subscribeHandler, &$unsubscribeHandler) {
+ if ($event === 'subscribe' && $subscribeHandler === null) {
+ $subscribeHandler = $callback;
+ }
+ if ($event === 'unsubscribe' && $unsubscribeHandler === null) {
+ $unsubscribeHandler = $callback;
+ }
+ });
$this->factory->expects($this->once())->method('createClient')->willReturn(\React\Promise\resolve($client));
$this->loop->expects($this->once())->method('addTimer');
$promise = $this->client->subscribe('foo');
- $client->emit('subscribe', array('foo', 1));
+ $this->assertTrue(is_callable($subscribeHandler));
+ $subscribeHandler('foo', 1);
$deferredSubscribe->resolve(array('subscribe', 'foo', 1));
$promise->then($this->expectCallableOnceWith(array('subscribe', 'foo', 1)));
$promise = $this->client->unsubscribe('foo');
- $client->emit('unsubscribe', array('foo', 0));
+ $this->assertTrue(is_callable($unsubscribeHandler));
+ $unsubscribeHandler('foo', 0);
$deferredUnsubscribe->resolve(array('unsubscribe', 'foo', 0));
$promise->then($this->expectCallableOnceWith(array('unsubscribe', 'foo', 0)));
}
+
+ public function createCallableMockWithOriginalConstructorDisabled($array)
+ {
+ if (method_exists('PHPUnit\Framework\MockObject\MockBuilder', 'addMethods')) {
+ // PHPUnit 9+
+ return $this->getMockBuilder('Clue\React\Redis\StreamingClient')->disableOriginalConstructor()->onlyMethods($array)->getMock();
+ } else {
+ // legacy PHPUnit 4 - PHPUnit 8
+ return $this->getMockBuilder('Clue\React\Redis\StreamingClient')->disableOriginalConstructor()->setMethods($array)->getMock();
+ }
+ }
}