aboutsummaryrefslogtreecommitdiffhomepage
path: root/assets/php/vendor/react/stream/tests
diff options
context:
space:
mode:
authormarvin-borner@live.com2018-04-10 21:50:16 +0200
committermarvin-borner@live.com2018-04-10 21:54:48 +0200
commitfc9401f04a3aca5abb22f87ebc210de8afe11d32 (patch)
treeb0b310f3581764ec3955f4e496a05137a32951c3 /assets/php/vendor/react/stream/tests
parent286d643180672f20526f3dc3bd19d7b751e2fa97 (diff)
Initial Commit
Diffstat (limited to 'assets/php/vendor/react/stream/tests')
-rw-r--r--assets/php/vendor/react/stream/tests/CallableStub.php10
-rw-r--r--assets/php/vendor/react/stream/tests/CompositeStreamTest.php267
-rw-r--r--assets/php/vendor/react/stream/tests/DuplexResourceStreamIntegrationTest.php352
-rw-r--r--assets/php/vendor/react/stream/tests/DuplexResourceStreamTest.php495
-rw-r--r--assets/php/vendor/react/stream/tests/EnforceBlockingWrapper.php35
-rw-r--r--assets/php/vendor/react/stream/tests/FunctionalInternetTest.php122
-rw-r--r--assets/php/vendor/react/stream/tests/ReadableResourceStreamTest.php372
-rw-r--r--assets/php/vendor/react/stream/tests/Stub/ReadableStreamStub.php61
-rw-r--r--assets/php/vendor/react/stream/tests/TestCase.php54
-rw-r--r--assets/php/vendor/react/stream/tests/ThroughStreamTest.php267
-rw-r--r--assets/php/vendor/react/stream/tests/UtilTest.php273
-rw-r--r--assets/php/vendor/react/stream/tests/WritableStreamResourceTest.php534
12 files changed, 2842 insertions, 0 deletions
diff --git a/assets/php/vendor/react/stream/tests/CallableStub.php b/assets/php/vendor/react/stream/tests/CallableStub.php
new file mode 100644
index 0000000..31cc834
--- /dev/null
+++ b/assets/php/vendor/react/stream/tests/CallableStub.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace React\Tests\Stream;
+
+class CallableStub
+{
+ public function __invoke()
+ {
+ }
+}
diff --git a/assets/php/vendor/react/stream/tests/CompositeStreamTest.php b/assets/php/vendor/react/stream/tests/CompositeStreamTest.php
new file mode 100644
index 0000000..df89c3e
--- /dev/null
+++ b/assets/php/vendor/react/stream/tests/CompositeStreamTest.php
@@ -0,0 +1,267 @@
+<?php
+
+namespace React\Tests\Stream;
+
+use React\Stream\CompositeStream;
+use React\Stream\ThroughStream;
+
+/**
+ * @covers React\Stream\CompositeStream
+ */
+class CompositeStreamTest extends TestCase
+{
+ /** @test */
+ public function itShouldCloseReadableIfNotWritable()
+ {
+ $readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+ $readable
+ ->expects($this->once())
+ ->method('isReadable')
+ ->willReturn(true);
+ $readable
+ ->expects($this->once())
+ ->method('close');
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->once())
+ ->method('isWritable')
+ ->willReturn(false);
+
+ $composite = new CompositeStream($readable, $writable);
+
+ $composite->on('close', $this->expectCallableNever());
+ $composite->close();
+ }
+
+ /** @test */
+ public function itShouldCloseWritableIfNotReadable()
+ {
+ $readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+ $readable
+ ->expects($this->once())
+ ->method('isReadable')
+ ->willReturn(false);
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->once())
+ ->method('close');
+
+ $composite = new CompositeStream($readable, $writable);
+
+ $composite->on('close', $this->expectCallableNever());
+ $composite->close();
+ }
+
+ /** @test */
+ public function itShouldForwardWritableCallsToWritableStream()
+ {
+ $readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+ $readable
+ ->expects($this->once())
+ ->method('isReadable')
+ ->willReturn(true);
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->once())
+ ->method('write')
+ ->with('foo');
+ $writable
+ ->expects($this->exactly(2))
+ ->method('isWritable')
+ ->willReturn(true);
+
+ $composite = new CompositeStream($readable, $writable);
+ $composite->write('foo');
+ $composite->isWritable();
+ }
+
+ /** @test */
+ public function itShouldForwardReadableCallsToReadableStream()
+ {
+ $readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+ $readable
+ ->expects($this->exactly(2))
+ ->method('isReadable')
+ ->willReturn(true);
+ $readable
+ ->expects($this->once())
+ ->method('pause');
+ $readable
+ ->expects($this->once())
+ ->method('resume');
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->any())
+ ->method('isWritable')
+ ->willReturn(true);
+
+ $composite = new CompositeStream($readable, $writable);
+ $composite->isReadable();
+ $composite->pause();
+ $composite->resume();
+ }
+
+ /** @test */
+ public function itShouldNotForwardResumeIfStreamIsNotWritable()
+ {
+ $readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+ $readable
+ ->expects($this->once())
+ ->method('isReadable')
+ ->willReturn(true);
+ $readable
+ ->expects($this->never())
+ ->method('resume');
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->exactly(2))
+ ->method('isWritable')
+ ->willReturnOnConsecutiveCalls(true, false);
+
+ $composite = new CompositeStream($readable, $writable);
+ $composite->resume();
+ }
+
+ /** @test */
+ public function endShouldDelegateToWritableWithData()
+ {
+ $readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+ $readable
+ ->expects($this->once())
+ ->method('isReadable')
+ ->willReturn(true);
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->once())
+ ->method('isWritable')
+ ->willReturn(true);
+ $writable
+ ->expects($this->once())
+ ->method('end')
+ ->with('foo');
+
+ $composite = new CompositeStream($readable, $writable);
+ $composite->end('foo');
+ }
+
+ /** @test */
+ public function closeShouldCloseBothStreams()
+ {
+ $readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+ $readable
+ ->expects($this->once())
+ ->method('isReadable')
+ ->willReturn(true);
+ $readable
+ ->expects($this->once())
+ ->method('close');
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->once())
+ ->method('isWritable')
+ ->willReturn(true);
+ $writable
+ ->expects($this->once())
+ ->method('close');
+
+ $composite = new CompositeStream($readable, $writable);
+ $composite->close();
+ }
+
+ /** @test */
+ public function itShouldForwardCloseOnlyOnce()
+ {
+ $readable = new ThroughStream();
+ $writable = new ThroughStream();
+
+ $composite = new CompositeStream($readable, $writable);
+ $composite->on('close', $this->expectCallableOnce());
+
+ $readable->close();
+ $writable->close();
+ }
+
+ /** @test */
+ public function itShouldForwardCloseAndRemoveAllListeners()
+ {
+ $in = new ThroughStream();
+
+ $composite = new CompositeStream($in, $in);
+ $composite->on('close', $this->expectCallableOnce());
+
+ $this->assertTrue($composite->isReadable());
+ $this->assertTrue($composite->isWritable());
+ $this->assertCount(1, $composite->listeners('close'));
+
+ $composite->close();
+
+ $this->assertFalse($composite->isReadable());
+ $this->assertFalse($composite->isWritable());
+ $this->assertCount(0, $composite->listeners('close'));
+ }
+
+ /** @test */
+ public function itShouldReceiveForwardedEvents()
+ {
+ $readable = new ThroughStream();
+ $writable = new ThroughStream();
+
+ $composite = new CompositeStream($readable, $writable);
+ $composite->on('data', $this->expectCallableOnce());
+ $composite->on('drain', $this->expectCallableOnce());
+
+ $readable->emit('data', array('foo'));
+ $writable->emit('drain');
+ }
+
+ /** @test */
+ public function itShouldHandlePipingCorrectly()
+ {
+ $readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+ $readable
+ ->expects($this->once())
+ ->method('isReadable')
+ ->willReturn(true);
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable->expects($this->any())->method('isWritable')->willReturn(True);
+ $writable
+ ->expects($this->once())
+ ->method('write')
+ ->with('foo');
+
+ $composite = new CompositeStream($readable, $writable);
+
+ $input = new ThroughStream();
+ $input->pipe($composite);
+ $input->emit('data', array('foo'));
+ }
+
+ /** @test */
+ public function itShouldForwardPipeCallsToReadableStream()
+ {
+ $readable = new ThroughStream();
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable->expects($this->any())->method('isWritable')->willReturn(True);
+
+ $composite = new CompositeStream($readable, $writable);
+
+ $output = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $output->expects($this->any())->method('isWritable')->willReturn(True);
+ $output
+ ->expects($this->once())
+ ->method('write')
+ ->with('foo');
+
+ $composite->pipe($output);
+ $readable->emit('data', array('foo'));
+ }
+}
diff --git a/assets/php/vendor/react/stream/tests/DuplexResourceStreamIntegrationTest.php b/assets/php/vendor/react/stream/tests/DuplexResourceStreamIntegrationTest.php
new file mode 100644
index 0000000..fb5f02a
--- /dev/null
+++ b/assets/php/vendor/react/stream/tests/DuplexResourceStreamIntegrationTest.php
@@ -0,0 +1,352 @@
+<?php
+
+namespace React\Tests\Stream;
+
+use React\Stream\DuplexResourceStream;
+use React\Stream\ReadableResourceStream;
+use React\EventLoop\ExtEventLoop;
+use React\EventLoop\ExtLibeventLoop;
+use React\EventLoop\ExtLibevLoop;
+use React\EventLoop\LoopInterface;
+use React\EventLoop\LibEventLoop;
+use React\EventLoop\LibEvLoop;
+use React\EventLoop\StreamSelectLoop;
+
+class DuplexResourceStreamIntegrationTest extends TestCase
+{
+ public function loopProvider()
+ {
+ return array(
+ array(
+ function() {
+ return true;
+ },
+ function () {
+ return new StreamSelectLoop();
+ }
+ ),
+ array(
+ function () {
+ return function_exists('event_base_new');
+ },
+ function () {
+ return class_exists('React\EventLoop\ExtLibeventLoop') ? new ExtLibeventLoop() : new LibEventLoop();
+ }
+ ),
+ array(
+ function () {
+ return class_exists('libev\EventLoop');
+ },
+ function () {
+ return class_exists('React\EventLoop\ExtLibevLoop') ? new ExtLibevLoop() : new LibEvLoop();
+ }
+ ),
+ array(
+ function () {
+ return class_exists('EventBase') && class_exists('React\EventLoop\ExtEventLoop');
+ },
+ function () {
+ return new ExtEventLoop();
+ }
+ )
+ );
+ }
+
+ /**
+ * @dataProvider loopProvider
+ */
+ public function testBufferReadsLargeChunks($condition, $loopFactory)
+ {
+ if (true !== $condition()) {
+ return $this->markTestSkipped('Loop implementation not available');
+ }
+
+ $loop = $loopFactory();
+
+ list($sockA, $sockB) = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, 0);
+
+ $bufferSize = 4096;
+ $streamA = new DuplexResourceStream($sockA, $loop, $bufferSize);
+ $streamB = new DuplexResourceStream($sockB, $loop, $bufferSize);
+
+ $testString = str_repeat("*", $bufferSize + 1);
+
+ $buffer = "";
+ $streamB->on('data', function ($data) use (&$buffer) {
+ $buffer .= $data;
+ });
+
+ $streamA->write($testString);
+
+ $this->loopTick($loop);
+ $this->loopTick($loop);
+ $this->loopTick($loop);
+
+ $streamA->close();
+ $streamB->close();
+
+ $this->assertEquals($testString, $buffer);
+ }
+
+ /**
+ * @dataProvider loopProvider
+ */
+ public function testWriteLargeChunk($condition, $loopFactory)
+ {
+ if (true !== $condition()) {
+ return $this->markTestSkipped('Loop implementation not available');
+ }
+
+ $loop = $loopFactory();
+
+ list($sockA, $sockB) = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, 0);
+
+ $streamA = new DuplexResourceStream($sockA, $loop);
+ $streamB = new DuplexResourceStream($sockB, $loop);
+
+ // limit seems to be 192 KiB
+ $size = 256 * 1024;
+
+ // sending side sends and expects clean close with no errors
+ $streamA->end(str_repeat('*', $size));
+ $streamA->on('close', $this->expectCallableOnce());
+ $streamA->on('error', $this->expectCallableNever());
+
+ // receiving side counts bytes and expects clean close with no errors
+ $received = 0;
+ $streamB->on('data', function ($chunk) use (&$received) {
+ $received += strlen($chunk);
+ });
+ $streamB->on('close', $this->expectCallableOnce());
+ $streamB->on('error', $this->expectCallableNever());
+
+ $loop->run();
+
+ $streamA->close();
+ $streamB->close();
+
+ $this->assertEquals($size, $received);
+ }
+
+ /**
+ * @dataProvider loopProvider
+ */
+ public function testDoesNotEmitDataIfNothingHasBeenWritten($condition, $loopFactory)
+ {
+ if (true !== $condition()) {
+ return $this->markTestSkipped('Loop implementation not available');
+ }
+
+ $loop = $loopFactory();
+
+ list($sockA, $sockB) = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, 0);
+
+ $streamA = new DuplexResourceStream($sockA, $loop);
+ $streamB = new DuplexResourceStream($sockB, $loop);
+
+ // end streamA without writing any data
+ $streamA->end();
+
+ // streamB should not emit any data
+ $streamB->on('data', $this->expectCallableNever());
+
+ $loop->run();
+
+ $streamA->close();
+ $streamB->close();
+ }
+
+ /**
+ * @dataProvider loopProvider
+ */
+ public function testDoesNotWriteDataIfRemoteSideFromPairHasBeenClosed($condition, $loopFactory)
+ {
+ if (true !== $condition()) {
+ return $this->markTestSkipped('Loop implementation not available');
+ }
+
+ $loop = $loopFactory();
+
+ list($sockA, $sockB) = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, 0);
+
+ $streamA = new DuplexResourceStream($sockA, $loop);
+ $streamB = new DuplexResourceStream($sockB, $loop);
+
+ // end streamA without writing any data
+ $streamA->pause();
+ $streamA->write('hello');
+ $streamA->on('close', $this->expectCallableOnce());
+
+ $streamB->on('data', $this->expectCallableNever());
+ $streamB->close();
+
+ $loop->run();
+
+ $streamA->close();
+ $streamB->close();
+ }
+
+ /**
+ * @dataProvider loopProvider
+ */
+ public function testDoesNotWriteDataIfServerSideHasBeenClosed($condition, $loopFactory)
+ {
+ if (true !== $condition()) {
+ return $this->markTestSkipped('Loop implementation not available');
+ }
+
+ $loop = $loopFactory();
+
+ $server = stream_socket_server('tcp://127.0.0.1:0');
+
+ $client = stream_socket_client(stream_socket_get_name($server, false));
+ $peer = stream_socket_accept($server);
+
+ $streamA = new DuplexResourceStream($client, $loop);
+ $streamB = new DuplexResourceStream($peer, $loop);
+
+ // end streamA without writing any data
+ $streamA->pause();
+ $streamA->write('hello');
+ $streamA->on('close', $this->expectCallableOnce());
+
+ $streamB->on('data', $this->expectCallableNever());
+ $streamB->close();
+
+ $loop->run();
+
+ $streamA->close();
+ $streamB->close();
+ }
+
+ /**
+ * @dataProvider loopProvider
+ */
+ public function testDoesNotWriteDataIfClientSideHasBeenClosed($condition, $loopFactory)
+ {
+ if (true !== $condition()) {
+ return $this->markTestSkipped('Loop implementation not available');
+ }
+
+ $loop = $loopFactory();
+
+ $server = stream_socket_server('tcp://127.0.0.1:0');
+
+ $client = stream_socket_client(stream_socket_get_name($server, false));
+ $peer = stream_socket_accept($server);
+
+ $streamA = new DuplexResourceStream($peer, $loop);
+ $streamB = new DuplexResourceStream($client, $loop);
+
+ // end streamA without writing any data
+ $streamA->pause();
+ $streamA->write('hello');
+ $streamA->on('close', $this->expectCallableOnce());
+
+ $streamB->on('data', $this->expectCallableNever());
+ $streamB->close();
+
+ $loop->run();
+
+ $streamA->close();
+ $streamB->close();
+ }
+
+ /**
+ * @dataProvider loopProvider
+ */
+ public function testReadsSingleChunkFromProcessPipe($condition, $loopFactory)
+ {
+ if (true !== $condition()) {
+ return $this->markTestSkipped('Loop implementation not available');
+ }
+
+ $loop = $loopFactory();
+
+ $stream = new ReadableResourceStream(popen('echo test', 'r'), $loop);
+ $stream->on('data', $this->expectCallableOnceWith("test\n"));
+ $stream->on('end', $this->expectCallableOnce());
+ $stream->on('error', $this->expectCallableNever());
+
+ $loop->run();
+ }
+
+ /**
+ * @dataProvider loopProvider
+ */
+ public function testReadsMultipleChunksFromProcessPipe($condition, $loopFactory)
+ {
+ if (true !== $condition()) {
+ return $this->markTestSkipped('Loop implementation not available');
+ }
+
+ $loop = $loopFactory();
+
+ $stream = new ReadableResourceStream(popen('echo a;sleep 0.1;echo b;sleep 0.1;echo c', 'r'), $loop);
+
+ $buffer = '';
+ $stream->on('data', function ($chunk) use (&$buffer) {
+ $buffer .= $chunk;
+ });
+
+ $stream->on('end', $this->expectCallableOnce());
+ $stream->on('error', $this->expectCallableNever());
+
+ $loop->run();
+
+ $this->assertEquals("a\n" . "b\n" . "c\n", $buffer);
+ }
+
+ /**
+ * @dataProvider loopProvider
+ */
+ public function testReadsLongChunksFromProcessPipe($condition, $loopFactory)
+ {
+ if (true !== $condition()) {
+ return $this->markTestSkipped('Loop implementation not available');
+ }
+
+ $loop = $loopFactory();
+
+ $stream = new ReadableResourceStream(popen('dd if=/dev/zero bs=12345 count=1234 2>&-', 'r'), $loop);
+
+ $bytes = 0;
+ $stream->on('data', function ($chunk) use (&$bytes) {
+ $bytes += strlen($chunk);
+ });
+
+ $stream->on('end', $this->expectCallableOnce());
+ $stream->on('error', $this->expectCallableNever());
+
+ $loop->run();
+
+ $this->assertEquals(12345 * 1234, $bytes);
+ }
+
+ /**
+ * @dataProvider loopProvider
+ */
+ public function testReadsNothingFromProcessPipeWithNoOutput($condition, $loopFactory)
+ {
+ if (true !== $condition()) {
+ return $this->markTestSkipped('Loop implementation not available');
+ }
+
+ $loop = $loopFactory();
+
+ $stream = new ReadableResourceStream(popen('true', 'r'), $loop);
+ $stream->on('data', $this->expectCallableNever());
+ $stream->on('end', $this->expectCallableOnce());
+ $stream->on('error', $this->expectCallableNever());
+
+ $loop->run();
+ }
+
+ private function loopTick(LoopInterface $loop)
+ {
+ $loop->addTimer(0, function () use ($loop) {
+ $loop->stop();
+ });
+ $loop->run();
+ }
+}
diff --git a/assets/php/vendor/react/stream/tests/DuplexResourceStreamTest.php b/assets/php/vendor/react/stream/tests/DuplexResourceStreamTest.php
new file mode 100644
index 0000000..3212ae8
--- /dev/null
+++ b/assets/php/vendor/react/stream/tests/DuplexResourceStreamTest.php
@@ -0,0 +1,495 @@
+<?php
+
+namespace React\Tests\Stream;
+
+use React\Stream\DuplexResourceStream;
+use Clue\StreamFilter as Filter;
+use React\Stream\WritableResourceStream;
+
+class DuplexResourceStreamTest extends TestCase
+{
+ /**
+ * @covers React\Stream\DuplexResourceStream::__construct
+ * @doesNotPerformAssertions
+ */
+ public function testConstructor()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ new DuplexResourceStream($stream, $loop);
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::__construct
+ * @doesNotPerformAssertions
+ */
+ public function testConstructorWithExcessiveMode()
+ {
+ // excessive flags are ignored for temp streams, so we have to use a file stream
+ $name = tempnam(sys_get_temp_dir(), 'test');
+ $stream = @fopen($name, 'r+eANYTHING');
+ unlink($name);
+
+ $loop = $this->createLoopMock();
+ $buffer = new DuplexResourceStream($stream, $loop);
+ $buffer->close();
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::__construct
+ * @expectedException InvalidArgumentException
+ */
+ public function testConstructorThrowsExceptionOnInvalidStream()
+ {
+ $loop = $this->createLoopMock();
+
+ new DuplexResourceStream('breakme', $loop);
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::__construct
+ * @expectedException InvalidArgumentException
+ */
+ public function testConstructorThrowsExceptionOnWriteOnlyStream()
+ {
+ if (defined('HHVM_VERSION')) {
+ $this->markTestSkipped('HHVM does not report fopen mode for STDOUT');
+ }
+
+ $loop = $this->createLoopMock();
+
+ new DuplexResourceStream(STDOUT, $loop);
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::__construct
+ * @expectedException InvalidArgumentException
+ */
+ public function testConstructorThrowsExceptionOnWriteOnlyStreamWithExcessiveMode()
+ {
+ // excessive flags are ignored for temp streams, so we have to use a file stream
+ $name = tempnam(sys_get_temp_dir(), 'test');
+ $stream = fopen($name, 'weANYTHING');
+ unlink($name);
+
+ $loop = $this->createLoopMock();
+ new DuplexResourceStream($stream, $loop);
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::__construct
+ * @expectedException RunTimeException
+ */
+ public function testConstructorThrowsExceptionIfStreamDoesNotSupportNonBlocking()
+ {
+ if (!in_array('blocking', stream_get_wrappers())) {
+ stream_wrapper_register('blocking', 'React\Tests\Stream\EnforceBlockingWrapper');
+ }
+
+ $stream = fopen('blocking://test', 'r+');
+ $loop = $this->createLoopMock();
+
+ new DuplexResourceStream($stream, $loop);
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::__construct
+ * @doesNotPerformAssertions
+ */
+ public function testConstructorAcceptsBuffer()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+
+ $conn = new DuplexResourceStream($stream, $loop, null, $buffer);
+ }
+
+ public function testCloseShouldEmitCloseEvent()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->on('close', $this->expectCallableOnce());
+ $conn->on('end', $this->expectCallableNever());
+
+ $conn->close();
+
+ $this->assertFalse($conn->isReadable());
+ }
+
+ public function testEndShouldEndBuffer()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $buffer->expects($this->once())->method('end')->with('foo');
+
+ $conn = new DuplexResourceStream($stream, $loop, null, $buffer);
+ $conn->end('foo');
+ }
+
+
+ public function testEndAfterCloseIsNoOp()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $buffer->expects($this->never())->method('end');
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->close();
+ $conn->end();
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::__construct
+ * @covers React\Stream\DuplexResourceStream::handleData
+ */
+ public function testDataEvent()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $capturedData = null;
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->on('data', function ($data) use (&$capturedData) {
+ $capturedData = $data;
+ });
+
+ fwrite($stream, "foobar\n");
+ rewind($stream);
+
+ $conn->handleData($stream);
+ $this->assertSame("foobar\n", $capturedData);
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::__construct
+ * @covers React\Stream\DuplexResourceStream::handleData
+ */
+ public function testDataEventDoesEmitOneChunkMatchingBufferSize()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $capturedData = null;
+
+ $conn = new DuplexResourceStream($stream, $loop, 4321);
+ $conn->on('data', function ($data) use (&$capturedData) {
+ $capturedData = $data;
+ });
+
+ fwrite($stream, str_repeat("a", 100000));
+ rewind($stream);
+
+ $conn->handleData($stream);
+
+ $this->assertTrue($conn->isReadable());
+ $this->assertEquals(4321, strlen($capturedData));
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::__construct
+ * @covers React\Stream\DuplexResourceStream::handleData
+ */
+ public function testDataEventDoesEmitOneChunkUntilStreamEndsWhenBufferSizeIsInfinite()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $capturedData = null;
+
+ $conn = new DuplexResourceStream($stream, $loop, -1);
+
+ $conn->on('data', function ($data) use (&$capturedData) {
+ $capturedData = $data;
+ });
+
+ fwrite($stream, str_repeat("a", 100000));
+ rewind($stream);
+
+ $conn->handleData($stream);
+
+ $this->assertTrue($conn->isReadable());
+ $this->assertEquals(100000, strlen($capturedData));
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::handleData
+ */
+ public function testEmptyStreamShouldNotEmitData()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->on('data', $this->expectCallableNever());
+
+ $conn->handleData($stream);
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::write
+ */
+ public function testWrite()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createWriteableLoopMock();
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->write("foo\n");
+
+ rewind($stream);
+ $this->assertSame("foo\n", fgets($stream));
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::end
+ * @covers React\Stream\DuplexResourceStream::isReadable
+ * @covers React\Stream\DuplexResourceStream::isWritable
+ */
+ public function testEnd()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->end();
+
+ $this->assertFalse(is_resource($stream));
+ $this->assertFalse($conn->isReadable());
+ $this->assertFalse($conn->isWritable());
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::end
+ */
+ public function testEndRemovesReadStreamFromLoop()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('addReadStream')->with($stream);
+ $loop->expects($this->once())->method('removeReadStream')->with($stream);
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->end('bye');
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::pause
+ */
+ public function testPauseRemovesReadStreamFromLoop()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('addReadStream')->with($stream);
+ $loop->expects($this->once())->method('removeReadStream')->with($stream);
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->pause();
+ $conn->pause();
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::pause
+ */
+ public function testResumeDoesAddStreamToLoopOnlyOnce()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('addReadStream')->with($stream);
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->resume();
+ $conn->resume();
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::close
+ */
+ public function testCloseRemovesReadStreamFromLoop()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('addReadStream')->with($stream);
+ $loop->expects($this->once())->method('removeReadStream')->with($stream);
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->close();
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::close
+ */
+ public function testCloseAfterPauseRemovesReadStreamFromLoopOnlyOnce()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('addReadStream')->with($stream);
+ $loop->expects($this->once())->method('removeReadStream')->with($stream);
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->pause();
+ $conn->close();
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::close
+ */
+ public function testResumeAfterCloseDoesAddReadStreamToLoopOnlyOnce()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('addReadStream')->with($stream);
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->close();
+ $conn->resume();
+ }
+
+ public function testEndedStreamsShouldNotWrite()
+ {
+ $file = tempnam(sys_get_temp_dir(), 'reactphptest_');
+ $stream = fopen($file, 'r+');
+ $loop = $this->createWriteableLoopMock();
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->write("foo\n");
+ $conn->end();
+
+ $res = $conn->write("bar\n");
+ $stream = fopen($file, 'r');
+
+ $this->assertSame("foo\n", fgets($stream));
+ $this->assertFalse($res);
+
+ unlink($file);
+ }
+
+ public function testPipeShouldReturnDestination()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $dest = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+
+ $this->assertSame($dest, $conn->pipe($dest));
+ }
+
+ public function testBufferEventsShouldBubbleUp()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop);
+ $conn = new DuplexResourceStream($stream, $loop, null, $buffer);
+
+ $conn->on('drain', $this->expectCallableOnce());
+ $conn->on('error', $this->expectCallableOnce());
+
+ $buffer->emit('drain');
+ $buffer->emit('error', array(new \RuntimeException('Whoops')));
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::handleData
+ */
+ public function testClosingStreamInDataEventShouldNotTriggerError()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->on('error', $this->expectCallableNever());
+ $conn->on('data', function ($data) use ($conn) {
+ $conn->close();
+ });
+
+ fwrite($stream, "foobar\n");
+ rewind($stream);
+
+ $conn->handleData($stream);
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::handleData
+ */
+ public function testDataFiltered()
+ {
+ $stream = fopen('php://temp', 'r+');
+
+ // add a filter which removes every 'a' when reading
+ Filter\append($stream, function ($chunk) {
+ return str_replace('a', '', $chunk);
+ }, STREAM_FILTER_READ);
+
+ $loop = $this->createLoopMock();
+
+ $capturedData = null;
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->on('data', function ($data) use (&$capturedData) {
+ $capturedData = $data;
+ });
+
+ fwrite($stream, "foobar\n");
+ rewind($stream);
+
+ $conn->handleData($stream);
+ $this->assertSame("foobr\n", $capturedData);
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::handleData
+ */
+ public function testDataErrorShouldEmitErrorAndClose()
+ {
+ $stream = fopen('php://temp', 'r+');
+
+ // add a filter which returns an error when encountering an 'a' when reading
+ Filter\append($stream, function ($chunk) {
+ if (strpos($chunk, 'a') !== false) {
+ throw new \Exception('Invalid');
+ }
+ return $chunk;
+ }, STREAM_FILTER_READ);
+
+ $loop = $this->createLoopMock();
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->on('data', $this->expectCallableNever());
+ $conn->on('error', $this->expectCallableOnce());
+ $conn->on('close', $this->expectCallableOnce());
+
+ fwrite($stream, "foobar\n");
+ rewind($stream);
+
+ $conn->handleData($stream);
+ }
+
+ private function createWriteableLoopMock()
+ {
+ $loop = $this->createLoopMock();
+ $loop
+ ->expects($this->once())
+ ->method('addWriteStream')
+ ->will($this->returnCallback(function ($stream, $listener) {
+ call_user_func($listener, $stream);
+ }));
+
+ return $loop;
+ }
+
+ private function createLoopMock()
+ {
+ return $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ }
+}
diff --git a/assets/php/vendor/react/stream/tests/EnforceBlockingWrapper.php b/assets/php/vendor/react/stream/tests/EnforceBlockingWrapper.php
new file mode 100644
index 0000000..39c0487
--- /dev/null
+++ b/assets/php/vendor/react/stream/tests/EnforceBlockingWrapper.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace React\Tests\Stream;
+
+/**
+ * Used to test dummy stream resources that do not support setting non-blocking mode
+ *
+ * @link http://php.net/manual/de/class.streamwrapper.php
+ */
+class EnforceBlockingWrapper
+{
+ public function stream_open($path, $mode, $options, &$opened_path)
+ {
+ return true;
+ }
+
+ public function stream_cast($cast_as)
+ {
+ return false;
+ }
+
+ public function stream_eof()
+ {
+ return false;
+ }
+
+ public function stream_set_option($option, $arg1, $arg2)
+ {
+ if ($option === STREAM_OPTION_BLOCKING) {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/assets/php/vendor/react/stream/tests/FunctionalInternetTest.php b/assets/php/vendor/react/stream/tests/FunctionalInternetTest.php
new file mode 100644
index 0000000..4d31e8e
--- /dev/null
+++ b/assets/php/vendor/react/stream/tests/FunctionalInternetTest.php
@@ -0,0 +1,122 @@
+<?php
+
+namespace React\Tests\Stream;
+
+use React\EventLoop\Factory;
+use React\EventLoop\LoopInterface;
+use React\Stream\DuplexResourceStream;
+use React\Stream\WritableResourceStream;
+
+/**
+ * @group internet
+ */
+class FunctionalInternetTest extends TestCase
+{
+ public function testUploadKilobytePlain()
+ {
+ $size = 1000;
+ $stream = stream_socket_client('tcp://httpbin.org:80');
+
+ $loop = Factory::create();
+ $stream = new DuplexResourceStream($stream, $loop);
+
+ $buffer = '';
+ $stream->on('data', function ($chunk) use (&$buffer) {
+ $buffer .= $chunk;
+ });
+
+ $stream->on('error', $this->expectCallableNever());
+
+ $stream->write("POST /post HTTP/1.0\r\nHost: httpbin.org\r\nContent-Length: $size\r\n\r\n" . str_repeat('.', $size));
+
+ $this->awaitStreamClose($stream, $loop);
+
+ $this->assertNotEquals('', $buffer);
+ }
+
+ public function testUploadBiggerBlockPlain()
+ {
+ $size = 50 * 1000;
+ $stream = stream_socket_client('tcp://httpbin.org:80');
+
+ $loop = Factory::create();
+ $stream = new DuplexResourceStream($stream, $loop);
+
+ $buffer = '';
+ $stream->on('data', function ($chunk) use (&$buffer) {
+ $buffer .= $chunk;
+ });
+
+ $stream->on('error', $this->expectCallableNever());
+
+ $stream->write("POST /post HTTP/1.0\r\nHost: httpbin.org\r\nContent-Length: $size\r\n\r\n" . str_repeat('.', $size));
+
+ $this->awaitStreamClose($stream, $loop);
+
+ $this->assertNotEquals('', $buffer);
+ }
+
+ public function testUploadKilobyteSecure()
+ {
+ $size = 1000;
+ $stream = stream_socket_client('tls://httpbin.org:443');
+
+ $loop = Factory::create();
+ $stream = new DuplexResourceStream($stream, $loop);
+
+ $buffer = '';
+ $stream->on('data', function ($chunk) use (&$buffer) {
+ $buffer .= $chunk;
+ });
+
+ $stream->on('error', $this->expectCallableNever());
+
+ $stream->write("POST /post HTTP/1.0\r\nHost: httpbin.org\r\nContent-Length: $size\r\n\r\n" . str_repeat('.', $size));
+
+ $this->awaitStreamClose($stream, $loop);
+
+ $this->assertNotEquals('', $buffer);
+ }
+
+ public function testUploadBiggerBlockSecureRequiresSmallerChunkSize()
+ {
+ $size = 50 * 1000;
+ $stream = stream_socket_client('tls://httpbin.org:443');
+
+ $loop = Factory::create();
+ $stream = new DuplexResourceStream(
+ $stream,
+ $loop,
+ null,
+ new WritableResourceStream($stream, $loop, null, 8192)
+ );
+
+ $buffer = '';
+ $stream->on('data', function ($chunk) use (&$buffer) {
+ $buffer .= $chunk;
+ });
+
+ $stream->on('error', $this->expectCallableNever());
+
+ $stream->write("POST /post HTTP/1.0\r\nHost: httpbin.org\r\nContent-Length: $size\r\n\r\n" . str_repeat('.', $size));
+
+ $this->awaitStreamClose($stream, $loop);
+
+ $this->assertNotEquals('', $buffer);
+ }
+
+ private function awaitStreamClose(DuplexResourceStream $stream, LoopInterface $loop, $timeout = 10.0)
+ {
+ $stream->on('close', function () use ($loop) {
+ $loop->stop();
+ });
+
+ $that = $this;
+ $loop->addTimer($timeout, function () use ($loop, $that) {
+ $loop->stop();
+ $that->fail('Timed out while waiting for stream to close');
+ });
+
+ $loop->run();
+ }
+}
diff --git a/assets/php/vendor/react/stream/tests/ReadableResourceStreamTest.php b/assets/php/vendor/react/stream/tests/ReadableResourceStreamTest.php
new file mode 100644
index 0000000..20da96f
--- /dev/null
+++ b/assets/php/vendor/react/stream/tests/ReadableResourceStreamTest.php
@@ -0,0 +1,372 @@
+<?php
+
+namespace React\Tests\Stream;
+
+use React\Stream\ReadableResourceStream;
+use Clue\StreamFilter as Filter;
+
+class ReadableResourceStreamTest extends TestCase
+{
+ /**
+ * @covers React\Stream\ReadableResourceStream::__construct
+ * @doesNotPerformAssertions
+ */
+ public function testConstructor()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ new ReadableResourceStream($stream, $loop);
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::__construct
+ * @doesNotPerformAssertions
+ */
+ public function testConstructorWithExcessiveMode()
+ {
+ // excessive flags are ignored for temp streams, so we have to use a file stream
+ $name = tempnam(sys_get_temp_dir(), 'test');
+ $stream = @fopen($name, 'r+eANYTHING');
+ unlink($name);
+
+ $loop = $this->createLoopMock();
+ $buffer = new ReadableResourceStream($stream, $loop);
+ $buffer->close();
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::__construct
+ * @expectedException InvalidArgumentException
+ */
+ public function testConstructorThrowsExceptionOnInvalidStream()
+ {
+ $loop = $this->createLoopMock();
+
+ new ReadableResourceStream(false, $loop);
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::__construct
+ * @expectedException InvalidArgumentException
+ */
+ public function testConstructorThrowsExceptionOnWriteOnlyStream()
+ {
+ if (defined('HHVM_VERSION')) {
+ $this->markTestSkipped('HHVM does not report fopen mode for STDOUT');
+ }
+
+ $loop = $this->createLoopMock();
+
+ new ReadableResourceStream(STDOUT, $loop);
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::__construct
+ * @expectedException InvalidArgumentException
+ */
+ public function testConstructorThrowsExceptionOnWriteOnlyStreamWithExcessiveMode()
+ {
+ // excessive flags are ignored for temp streams, so we have to use a file stream
+ $name = tempnam(sys_get_temp_dir(), 'test');
+ $stream = fopen($name, 'weANYTHING');
+ unlink($name);
+
+ $loop = $this->createLoopMock();
+ new ReadableResourceStream($stream, $loop);
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::__construct
+ * @expectedException RuntimeException
+ */
+ public function testConstructorThrowsExceptionIfStreamDoesNotSupportNonBlocking()
+ {
+ if (!in_array('blocking', stream_get_wrappers())) {
+ stream_wrapper_register('blocking', 'React\Tests\Stream\EnforceBlockingWrapper');
+ }
+
+ $stream = fopen('blocking://test', 'r+');
+ $loop = $this->createLoopMock();
+
+ new ReadableResourceStream($stream, $loop);
+ }
+
+
+ public function testCloseShouldEmitCloseEvent()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $conn->on('close', $this->expectCallableOnce());
+
+ $conn->close();
+
+ $this->assertFalse($conn->isReadable());
+ }
+
+ public function testCloseTwiceShouldEmitCloseEventOnce()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $conn->on('close', $this->expectCallableOnce());
+
+ $conn->close();
+ $conn->close();
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::__construct
+ * @covers React\Stream\ReadableResourceStream::handleData
+ */
+ public function testDataEvent()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $capturedData = null;
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $conn->on('data', function ($data) use (&$capturedData) {
+ $capturedData = $data;
+ });
+
+ fwrite($stream, "foobar\n");
+ rewind($stream);
+
+ $conn->handleData($stream);
+ $this->assertSame("foobar\n", $capturedData);
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::__construct
+ * @covers React\Stream\ReadableResourceStream::handleData
+ */
+ public function testDataEventDoesEmitOneChunkMatchingBufferSize()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $capturedData = null;
+
+ $conn = new ReadableResourceStream($stream, $loop, 4321);
+ $conn->on('data', function ($data) use (&$capturedData) {
+ $capturedData = $data;
+ });
+
+ fwrite($stream, str_repeat("a", 100000));
+ rewind($stream);
+
+ $conn->handleData($stream);
+
+ $this->assertTrue($conn->isReadable());
+ $this->assertEquals(4321, strlen($capturedData));
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::__construct
+ * @covers React\Stream\ReadableResourceStream::handleData
+ */
+ public function testDataEventDoesEmitOneChunkUntilStreamEndsWhenBufferSizeIsInfinite()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $capturedData = null;
+
+ $conn = new ReadableResourceStream($stream, $loop, -1);
+
+ $conn->on('data', function ($data) use (&$capturedData) {
+ $capturedData = $data;
+ });
+
+ fwrite($stream, str_repeat("a", 100000));
+ rewind($stream);
+
+ $conn->handleData($stream);
+
+ $this->assertTrue($conn->isReadable());
+ $this->assertEquals(100000, strlen($capturedData));
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::handleData
+ */
+ public function testEmptyStreamShouldNotEmitData()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $conn->on('data', $this->expectCallableNever());
+
+ $conn->handleData($stream);
+ }
+
+ public function testPipeShouldReturnDestination()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $dest = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+
+ $this->assertSame($dest, $conn->pipe($dest));
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::handleData
+ */
+ public function testClosingStreamInDataEventShouldNotTriggerError()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $conn->on('error', $this->expectCallableNever());
+ $conn->on('data', function ($data) use ($conn) {
+ $conn->close();
+ });
+
+ fwrite($stream, "foobar\n");
+ rewind($stream);
+
+ $conn->handleData($stream);
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::pause
+ */
+ public function testPauseRemovesReadStreamFromLoop()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('addReadStream')->with($stream);
+ $loop->expects($this->once())->method('removeReadStream')->with($stream);
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $conn->pause();
+ $conn->pause();
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::pause
+ */
+ public function testResumeDoesAddStreamToLoopOnlyOnce()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('addReadStream')->with($stream);
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $conn->resume();
+ $conn->resume();
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::close
+ */
+ public function testCloseRemovesReadStreamFromLoop()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('addReadStream')->with($stream);
+ $loop->expects($this->once())->method('removeReadStream')->with($stream);
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $conn->close();
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::close
+ */
+ public function testCloseAfterPauseRemovesReadStreamFromLoopOnce()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('addReadStream')->with($stream);
+ $loop->expects($this->once())->method('removeReadStream')->with($stream);
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $conn->pause();
+ $conn->close();
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::close
+ */
+ public function testResumeAfterCloseDoesAddReadStreamToLoopOnlyOnce()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('addReadStream')->with($stream);
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $conn->close();
+ $conn->resume();
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::handleData
+ */
+ public function testDataFiltered()
+ {
+ $stream = fopen('php://temp', 'r+');
+
+ // add a filter which removes every 'a' when reading
+ Filter\append($stream, function ($chunk) {
+ return str_replace('a', '', $chunk);
+ }, STREAM_FILTER_READ);
+
+ $loop = $this->createLoopMock();
+
+ $capturedData = null;
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $conn->on('data', function ($data) use (&$capturedData) {
+ $capturedData = $data;
+ });
+
+ fwrite($stream, "foobar\n");
+ rewind($stream);
+
+ $conn->handleData($stream);
+ $this->assertSame("foobr\n", $capturedData);
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::handleData
+ */
+ public function testDataErrorShouldEmitErrorAndClose()
+ {
+ $stream = fopen('php://temp', 'r+');
+
+ // add a filter which returns an error when encountering an 'a' when reading
+ Filter\append($stream, function ($chunk) {
+ if (strpos($chunk, 'a') !== false) {
+ throw new \Exception('Invalid');
+ }
+ return $chunk;
+ }, STREAM_FILTER_READ);
+
+ $loop = $this->createLoopMock();
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $conn->on('data', $this->expectCallableNever());
+ $conn->on('error', $this->expectCallableOnce());
+ $conn->on('close', $this->expectCallableOnce());
+
+ fwrite($stream, "foobar\n");
+ rewind($stream);
+
+ $conn->handleData($stream);
+ }
+
+ private function createLoopMock()
+ {
+ return $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ }
+}
diff --git a/assets/php/vendor/react/stream/tests/Stub/ReadableStreamStub.php b/assets/php/vendor/react/stream/tests/Stub/ReadableStreamStub.php
new file mode 100644
index 0000000..6984f24
--- /dev/null
+++ b/assets/php/vendor/react/stream/tests/Stub/ReadableStreamStub.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace React\Tests\Stream\Stub;
+
+use Evenement\EventEmitter;
+use React\Stream\ReadableStreamInterface;
+use React\Stream\WritableStreamInterface;
+use React\Stream\Util;
+
+class ReadableStreamStub extends EventEmitter implements ReadableStreamInterface
+{
+ public $readable = true;
+ public $paused = false;
+
+ public function isReadable()
+ {
+ return true;
+ }
+
+ // trigger data event
+ public function write($data)
+ {
+ $this->emit('data', array($data));
+ }
+
+ // trigger error event
+ public function error($error)
+ {
+ $this->emit('error', array($error));
+ }
+
+ // trigger end event
+ public function end()
+ {
+ $this->emit('end', array());
+ }
+
+ public function pause()
+ {
+ $this->paused = true;
+ }
+
+ public function resume()
+ {
+ $this->paused = false;
+ }
+
+ public function close()
+ {
+ $this->readable = false;
+
+ $this->emit('close');
+ }
+
+ public function pipe(WritableStreamInterface $dest, array $options = array())
+ {
+ Util::pipe($this, $dest, $options);
+
+ return $dest;
+ }
+}
diff --git a/assets/php/vendor/react/stream/tests/TestCase.php b/assets/php/vendor/react/stream/tests/TestCase.php
new file mode 100644
index 0000000..c8fc1db
--- /dev/null
+++ b/assets/php/vendor/react/stream/tests/TestCase.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace React\Tests\Stream;
+
+use PHPUnit\Framework\TestCase as BaseTestCase;
+
+class TestCase extends BaseTestCase
+{
+ protected function expectCallableExactly($amount)
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->exactly($amount))
+ ->method('__invoke');
+
+ return $mock;
+ }
+
+ protected function expectCallableOnce()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke');
+
+ return $mock;
+ }
+
+ protected function expectCallableOnceWith($value)
+ {
+ $callback = $this->createCallableMock();
+ $callback
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($value);
+
+ return $callback;
+ }
+
+ protected function expectCallableNever()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->never())
+ ->method('__invoke');
+
+ return $mock;
+ }
+
+ protected function createCallableMock()
+ {
+ return $this->getMockBuilder('React\Tests\Stream\CallableStub')->getMock();
+ }
+}
diff --git a/assets/php/vendor/react/stream/tests/ThroughStreamTest.php b/assets/php/vendor/react/stream/tests/ThroughStreamTest.php
new file mode 100644
index 0000000..a98badf
--- /dev/null
+++ b/assets/php/vendor/react/stream/tests/ThroughStreamTest.php
@@ -0,0 +1,267 @@
+<?php
+
+namespace React\Tests\Stream;
+
+use React\Stream\ThroughStream;
+
+/**
+ * @covers React\Stream\ThroughStream
+ */
+class ThroughStreamTest extends TestCase
+{
+ /**
+ * @test
+ * @expectedException InvalidArgumentException
+ */
+ public function itShouldRejectInvalidCallback()
+ {
+ new ThroughStream(123);
+ }
+
+ /** @test */
+ public function itShouldReturnTrueForAnyDataWrittenToIt()
+ {
+ $through = new ThroughStream();
+ $ret = $through->write('foo');
+
+ $this->assertTrue($ret);
+ }
+
+ /** @test */
+ public function itShouldEmitAnyDataWrittenToIt()
+ {
+ $through = new ThroughStream();
+ $through->on('data', $this->expectCallableOnceWith('foo'));
+ $through->write('foo');
+ }
+
+ /** @test */
+ public function itShouldEmitAnyDataWrittenToItPassedThruFunction()
+ {
+ $through = new ThroughStream('strtoupper');
+ $through->on('data', $this->expectCallableOnceWith('FOO'));
+ $through->write('foo');
+ }
+
+ /** @test */
+ public function itShouldEmitAnyDataWrittenToItPassedThruCallback()
+ {
+ $through = new ThroughStream('strtoupper');
+ $through->on('data', $this->expectCallableOnceWith('FOO'));
+ $through->write('foo');
+ }
+
+ /** @test */
+ public function itShouldEmitErrorAndCloseIfCallbackThrowsException()
+ {
+ $through = new ThroughStream(function () {
+ throw new \RuntimeException();
+ });
+ $through->on('error', $this->expectCallableOnce());
+ $through->on('close', $this->expectCallableOnce());
+ $through->on('data', $this->expectCallableNever());
+ $through->on('end', $this->expectCallableNever());
+
+ $through->write('foo');
+
+ $this->assertFalse($through->isReadable());
+ $this->assertFalse($through->isWritable());
+ }
+
+ /** @test */
+ public function itShouldEmitErrorAndCloseIfCallbackThrowsExceptionOnEnd()
+ {
+ $through = new ThroughStream(function () {
+ throw new \RuntimeException();
+ });
+ $through->on('error', $this->expectCallableOnce());
+ $through->on('close', $this->expectCallableOnce());
+ $through->on('data', $this->expectCallableNever());
+ $through->on('end', $this->expectCallableNever());
+
+ $through->end('foo');
+
+ $this->assertFalse($through->isReadable());
+ $this->assertFalse($through->isWritable());
+ }
+
+ /** @test */
+ public function itShouldReturnFalseForAnyDataWrittenToItWhenPaused()
+ {
+ $through = new ThroughStream();
+ $through->pause();
+ $ret = $through->write('foo');
+
+ $this->assertFalse($ret);
+ }
+
+ /** @test */
+ public function itShouldEmitDrainOnResumeAfterReturnFalseForAnyDataWrittenToItWhenPaused()
+ {
+ $through = new ThroughStream();
+ $through->pause();
+ $through->write('foo');
+
+ $through->on('drain', $this->expectCallableOnce());
+ $through->resume();
+ }
+
+ /** @test */
+ public function itShouldReturnTrueForAnyDataWrittenToItWhenResumedAfterPause()
+ {
+ $through = new ThroughStream();
+ $through->on('drain', $this->expectCallableNever());
+ $through->pause();
+ $through->resume();
+ $ret = $through->write('foo');
+
+ $this->assertTrue($ret);
+ }
+
+ /** @test */
+ public function pipingStuffIntoItShouldWork()
+ {
+ $readable = new ThroughStream();
+
+ $through = new ThroughStream();
+ $through->on('data', $this->expectCallableOnceWith('foo'));
+
+ $readable->pipe($through);
+ $readable->emit('data', array('foo'));
+ }
+
+ /** @test */
+ public function endShouldEmitEndAndClose()
+ {
+ $through = new ThroughStream();
+ $through->on('data', $this->expectCallableNever());
+ $through->on('end', $this->expectCallableOnce());
+ $through->on('close', $this->expectCallableOnce());
+ $through->end();
+ }
+
+ /** @test */
+ public function endShouldCloseTheStream()
+ {
+ $through = new ThroughStream();
+ $through->on('data', $this->expectCallableNever());
+ $through->end();
+
+ $this->assertFalse($through->isReadable());
+ $this->assertFalse($through->isWritable());
+ }
+
+ /** @test */
+ public function endShouldWriteDataBeforeClosing()
+ {
+ $through = new ThroughStream();
+ $through->on('data', $this->expectCallableOnceWith('foo'));
+ $through->end('foo');
+
+ $this->assertFalse($through->isReadable());
+ $this->assertFalse($through->isWritable());
+ }
+
+ /** @test */
+ public function endTwiceShouldOnlyEmitOnce()
+ {
+ $through = new ThroughStream();
+ $through->on('data', $this->expectCallableOnce('first'));
+ $through->end('first');
+ $through->end('ignored');
+ }
+
+ /** @test */
+ public function writeAfterEndShouldReturnFalse()
+ {
+ $through = new ThroughStream();
+ $through->on('data', $this->expectCallableNever());
+ $through->end();
+
+ $this->assertFalse($through->write('foo'));
+ }
+
+ /** @test */
+ public function writeDataWillCloseStreamShouldReturnFalse()
+ {
+ $through = new ThroughStream();
+ $through->on('data', array($through, 'close'));
+
+ $this->assertFalse($through->write('foo'));
+ }
+
+ /** @test */
+ public function writeDataToPausedShouldReturnFalse()
+ {
+ $through = new ThroughStream();
+ $through->pause();
+
+ $this->assertFalse($through->write('foo'));
+ }
+
+ /** @test */
+ public function writeDataToResumedShouldReturnTrue()
+ {
+ $through = new ThroughStream();
+ $through->pause();
+ $through->resume();
+
+ $this->assertTrue($through->write('foo'));
+ }
+
+ /** @test */
+ public function itShouldBeReadableByDefault()
+ {
+ $through = new ThroughStream();
+ $this->assertTrue($through->isReadable());
+ }
+
+ /** @test */
+ public function itShouldBeWritableByDefault()
+ {
+ $through = new ThroughStream();
+ $this->assertTrue($through->isWritable());
+ }
+
+ /** @test */
+ public function closeShouldCloseOnce()
+ {
+ $through = new ThroughStream();
+
+ $through->on('close', $this->expectCallableOnce());
+
+ $through->close();
+
+ $this->assertFalse($through->isReadable());
+ $this->assertFalse($through->isWritable());
+ }
+
+ /** @test */
+ public function doubleCloseShouldCloseOnce()
+ {
+ $through = new ThroughStream();
+
+ $through->on('close', $this->expectCallableOnce());
+
+ $through->close();
+ $through->close();
+
+ $this->assertFalse($through->isReadable());
+ $this->assertFalse($through->isWritable());
+ }
+
+ /** @test */
+ public function pipeShouldPipeCorrectly()
+ {
+ $output = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $output->expects($this->any())->method('isWritable')->willReturn(True);
+ $output
+ ->expects($this->once())
+ ->method('write')
+ ->with('foo');
+
+ $through = new ThroughStream();
+ $through->pipe($output);
+ $through->write('foo');
+ }
+}
diff --git a/assets/php/vendor/react/stream/tests/UtilTest.php b/assets/php/vendor/react/stream/tests/UtilTest.php
new file mode 100644
index 0000000..3d113ab
--- /dev/null
+++ b/assets/php/vendor/react/stream/tests/UtilTest.php
@@ -0,0 +1,273 @@
+<?php
+
+namespace React\Tests\Stream;
+
+use React\Stream\WritableResourceStream;
+use React\Stream\Util;
+use React\Stream\CompositeStream;
+use React\Stream\ThroughStream;
+
+/**
+ * @covers React\Stream\Util
+ */
+class UtilTest extends TestCase
+{
+ public function testPipeReturnsDestinationStream()
+ {
+ $readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+
+ $ret = Util::pipe($readable, $writable);
+
+ $this->assertSame($writable, $ret);
+ }
+
+ public function testPipeNonReadableSourceShouldDoNothing()
+ {
+ $readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+ $readable
+ ->expects($this->any())
+ ->method('isReadable')
+ ->willReturn(false);
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->never())
+ ->method('isWritable');
+ $writable
+ ->expects($this->never())
+ ->method('end');
+
+ Util::pipe($readable, $writable);
+ }
+
+ public function testPipeIntoNonWritableDestinationShouldPauseSource()
+ {
+ $readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+ $readable
+ ->expects($this->any())
+ ->method('isReadable')
+ ->willReturn(true);
+ $readable
+ ->expects($this->once())
+ ->method('pause');
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->any())
+ ->method('isWritable')
+ ->willReturn(false);
+ $writable
+ ->expects($this->never())
+ ->method('end');
+
+ Util::pipe($readable, $writable);
+ }
+
+ public function testPipeClosingDestPausesSource()
+ {
+ $readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+ $readable
+ ->expects($this->any())
+ ->method('isReadable')
+ ->willReturn(true);
+ $readable
+ ->expects($this->once())
+ ->method('pause');
+
+ $writable = new ThroughStream();
+
+ Util::pipe($readable, $writable);
+
+ $writable->close();
+ }
+
+ public function testPipeWithEnd()
+ {
+ $readable = new Stub\ReadableStreamStub();
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->any())
+ ->method('isWritable')
+ ->willReturn(true);
+ $writable
+ ->expects($this->once())
+ ->method('end');
+
+ Util::pipe($readable, $writable);
+
+ $readable->end();
+ }
+
+ public function testPipeWithoutEnd()
+ {
+ $readable = new Stub\ReadableStreamStub();
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->any())
+ ->method('isWritable')
+ ->willReturn(true);
+ $writable
+ ->expects($this->never())
+ ->method('end');
+
+ Util::pipe($readable, $writable, array('end' => false));
+
+ $readable->end();
+ }
+
+ public function testPipeWithTooSlowWritableShouldPauseReadable()
+ {
+ $readable = new Stub\ReadableStreamStub();
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->any())
+ ->method('isWritable')
+ ->willReturn(true);
+ $writable
+ ->expects($this->once())
+ ->method('write')
+ ->with('some data')
+ ->will($this->returnValue(false));
+
+ $readable->pipe($writable);
+
+ $this->assertFalse($readable->paused);
+ $readable->write('some data');
+ $this->assertTrue($readable->paused);
+ }
+
+ public function testPipeWithTooSlowWritableShouldResumeOnDrain()
+ {
+ $readable = new Stub\ReadableStreamStub();
+
+ $onDrain = null;
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->any())
+ ->method('isWritable')
+ ->willReturn(true);
+ $writable
+ ->expects($this->any())
+ ->method('on')
+ ->will($this->returnCallback(function ($name, $callback) use (&$onDrain) {
+ if ($name === 'drain') {
+ $onDrain = $callback;
+ }
+ }));
+
+ $readable->pipe($writable);
+ $readable->pause();
+
+ $this->assertTrue($readable->paused);
+ $this->assertNotNull($onDrain);
+ $onDrain();
+ $this->assertFalse($readable->paused);
+ }
+
+ public function testPipeWithWritableResourceStream()
+ {
+ $readable = new Stub\ReadableStreamStub();
+
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $buffer = new WritableResourceStream($stream, $loop);
+
+ $readable->pipe($buffer);
+
+ $readable->write('hello, I am some ');
+ $readable->write('random data');
+
+ $buffer->handleWrite();
+ rewind($stream);
+ $this->assertSame('hello, I am some random data', stream_get_contents($stream));
+ }
+
+ public function testPipeSetsUpListeners()
+ {
+ $source = new ThroughStream();
+ $dest = new ThroughStream();
+
+ $this->assertCount(0, $source->listeners('data'));
+ $this->assertCount(0, $source->listeners('end'));
+ $this->assertCount(0, $dest->listeners('drain'));
+
+ Util::pipe($source, $dest);
+
+ $this->assertCount(1, $source->listeners('data'));
+ $this->assertCount(1, $source->listeners('end'));
+ $this->assertCount(1, $dest->listeners('drain'));
+ }
+
+ public function testPipeClosingSourceRemovesListeners()
+ {
+ $source = new ThroughStream();
+ $dest = new ThroughStream();
+
+ Util::pipe($source, $dest);
+
+ $source->close();
+
+ $this->assertCount(0, $source->listeners('data'));
+ $this->assertCount(0, $source->listeners('end'));
+ $this->assertCount(0, $dest->listeners('drain'));
+ }
+
+ public function testPipeClosingDestRemovesListeners()
+ {
+ $source = new ThroughStream();
+ $dest = new ThroughStream();
+
+ Util::pipe($source, $dest);
+
+ $dest->close();
+
+ $this->assertCount(0, $source->listeners('data'));
+ $this->assertCount(0, $source->listeners('end'));
+ $this->assertCount(0, $dest->listeners('drain'));
+ }
+
+ public function testPipeDuplexIntoSelfEndsOnEnd()
+ {
+ $readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+ $readable->expects($this->any())->method('isReadable')->willReturn(true);
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable->expects($this->any())->method('isWritable')->willReturn(true);
+ $duplex = new CompositeStream($readable, $writable);
+
+ Util::pipe($duplex, $duplex);
+
+ $writable->expects($this->once())->method('end');
+
+ $duplex->emit('end');
+ }
+
+ /** @test */
+ public function forwardEventsShouldSetupForwards()
+ {
+ $source = new ThroughStream();
+ $target = new ThroughStream();
+
+ Util::forwardEvents($source, $target, array('data'));
+ $target->on('data', $this->expectCallableOnce());
+ $target->on('foo', $this->expectCallableNever());
+
+ $source->emit('data', array('hello'));
+ $source->emit('foo', array('bar'));
+ }
+
+ private function createLoopMock()
+ {
+ return $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ }
+
+ private function notEqualTo($value)
+ {
+ return new \PHPUnit_Framework_Constraint_Not($value);
+ }
+}
diff --git a/assets/php/vendor/react/stream/tests/WritableStreamResourceTest.php b/assets/php/vendor/react/stream/tests/WritableStreamResourceTest.php
new file mode 100644
index 0000000..05bce9c
--- /dev/null
+++ b/assets/php/vendor/react/stream/tests/WritableStreamResourceTest.php
@@ -0,0 +1,534 @@
+<?php
+
+namespace React\Tests\Stream;
+
+use Clue\StreamFilter as Filter;
+use React\Stream\WritableResourceStream;
+
+class WritableResourceStreamTest extends TestCase
+{
+ /**
+ * @covers React\Stream\WritableResourceStream::__construct
+ * @doesNotPerformAssertions
+ */
+ public function testConstructor()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ new WritableResourceStream($stream, $loop);
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::__construct
+ * @doesNotPerformAssertions
+ */
+ public function testConstructorWithExcessiveMode()
+ {
+ // excessive flags are ignored for temp streams, so we have to use a file stream
+ $name = tempnam(sys_get_temp_dir(), 'test');
+ $stream = @fopen($name, 'w+eANYTHING');
+ unlink($name);
+
+ $loop = $this->createLoopMock();
+ $buffer = new WritableResourceStream($stream, $loop);
+ $buffer->close();
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::__construct
+ * @expectedException InvalidArgumentException
+ */
+ public function testConstructorThrowsIfNotAValidStreamResource()
+ {
+ $stream = null;
+ $loop = $this->createLoopMock();
+
+ new WritableResourceStream($stream, $loop);
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::__construct
+ * @expectedException InvalidArgumentException
+ */
+ public function testConstructorThrowsExceptionOnReadOnlyStream()
+ {
+ $stream = fopen('php://temp', 'r');
+ $loop = $this->createLoopMock();
+
+ new WritableResourceStream($stream, $loop);
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::__construct
+ * @expectedException InvalidArgumentException
+ */
+ public function testConstructorThrowsExceptionOnReadOnlyStreamWithExcessiveMode()
+ {
+ // excessive flags are ignored for temp streams, so we have to use a file stream
+ $name = tempnam(sys_get_temp_dir(), 'test');
+ $stream = fopen($name, 'reANYTHING');
+ unlink($name);
+
+ $loop = $this->createLoopMock();
+ new WritableResourceStream($stream, $loop);
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::__construct
+ * @expectedException RuntimeException
+ */
+ public function testConstructorThrowsExceptionIfStreamDoesNotSupportNonBlocking()
+ {
+ if (!in_array('blocking', stream_get_wrappers())) {
+ stream_wrapper_register('blocking', 'React\Tests\Stream\EnforceBlockingWrapper');
+ }
+
+ $stream = fopen('blocking://test', 'r+');
+ $loop = $this->createLoopMock();
+
+ new WritableResourceStream($stream, $loop);
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::write
+ * @covers React\Stream\WritableResourceStream::handleWrite
+ */
+ public function testWrite()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createWriteableLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop);
+ $buffer->on('error', $this->expectCallableNever());
+
+ $buffer->write("foobar\n");
+ rewind($stream);
+ $this->assertSame("foobar\n", fread($stream, 1024));
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::write
+ */
+ public function testWriteWithDataDoesAddResourceToLoop()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('addWriteStream')->with($this->equalTo($stream));
+
+ $buffer = new WritableResourceStream($stream, $loop);
+
+ $buffer->write("foobar\n");
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::write
+ * @covers React\Stream\WritableResourceStream::handleWrite
+ */
+ public function testEmptyWriteDoesNotAddToLoop()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->never())->method('addWriteStream');
+
+ $buffer = new WritableResourceStream($stream, $loop);
+
+ $buffer->write("");
+ $buffer->write(null);
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::write
+ * @covers React\Stream\WritableResourceStream::handleWrite
+ */
+ public function testWriteReturnsFalseWhenWritableResourceStreamIsFull()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createWriteableLoopMock();
+ $loop->preventWrites = true;
+
+ $buffer = new WritableResourceStream($stream, $loop, 4);
+ $buffer->on('error', $this->expectCallableNever());
+
+ $this->assertTrue($buffer->write("foo"));
+ $loop->preventWrites = false;
+ $this->assertFalse($buffer->write("bar\n"));
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::write
+ */
+ public function testWriteReturnsFalseWhenWritableResourceStreamIsExactlyFull()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop, 3);
+
+ $this->assertFalse($buffer->write("foo"));
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::write
+ * @covers React\Stream\WritableResourceStream::handleWrite
+ */
+ public function testWriteDetectsWhenOtherSideIsClosed()
+ {
+ list($a, $b) = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
+
+ $loop = $this->createWriteableLoopMock();
+
+ $buffer = new WritableResourceStream($a, $loop, 4);
+ $buffer->on('error', $this->expectCallableOnce());
+
+ fclose($b);
+
+ $buffer->write("foo");
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::write
+ * @covers React\Stream\WritableResourceStream::handleWrite
+ */
+ public function testEmitsDrainAfterWriteWhichExceedsBuffer()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop, 2);
+ $buffer->on('error', $this->expectCallableNever());
+ $buffer->on('drain', $this->expectCallableOnce());
+
+ $buffer->write("foo");
+ $buffer->handleWrite();
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::write
+ * @covers React\Stream\WritableResourceStream::handleWrite
+ */
+ public function testWriteInDrain()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop, 2);
+ $buffer->on('error', $this->expectCallableNever());
+
+ $buffer->once('drain', function () use ($buffer) {
+ $buffer->write("bar\n");
+ $buffer->handleWrite();
+ });
+
+ $this->assertFalse($buffer->write("foo\n"));
+ $buffer->handleWrite();
+
+ fseek($stream, 0);
+ $this->assertSame("foo\nbar\n", stream_get_contents($stream));
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::write
+ * @covers React\Stream\WritableResourceStream::handleWrite
+ */
+ public function testDrainAfterWrite()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop, 2);
+
+ $buffer->on('drain', $this->expectCallableOnce());
+
+ $buffer->write("foo");
+ $buffer->handleWrite();
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::handleWrite
+ */
+ public function testDrainAfterWriteWillRemoveResourceFromLoopWithoutClosing()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('removeWriteStream')->with($stream);
+
+ $buffer = new WritableResourceStream($stream, $loop, 2);
+
+ $buffer->on('drain', $this->expectCallableOnce());
+
+ $buffer->on('close', $this->expectCallableNever());
+
+ $buffer->write("foo");
+ $buffer->handleWrite();
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::handleWrite
+ */
+ public function testClosingDuringDrainAfterWriteWillRemoveResourceFromLoopOnceAndClose()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('removeWriteStream')->with($stream);
+
+ $buffer = new WritableResourceStream($stream, $loop, 2);
+
+ $buffer->on('drain', function () use ($buffer) {
+ $buffer->close();
+ });
+
+ $buffer->on('close', $this->expectCallableOnce());
+
+ $buffer->write("foo");
+ $buffer->handleWrite();
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::end
+ */
+ public function testEndWithoutDataClosesImmediatelyIfWritableResourceStreamIsEmpty()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop);
+ $buffer->on('error', $this->expectCallableNever());
+ $buffer->on('close', $this->expectCallableOnce());
+
+ $this->assertTrue($buffer->isWritable());
+ $buffer->end();
+ $this->assertFalse($buffer->isWritable());
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::end
+ */
+ public function testEndWithoutDataDoesNotCloseIfWritableResourceStreamIsFull()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop);
+ $buffer->on('error', $this->expectCallableNever());
+ $buffer->on('close', $this->expectCallableNever());
+
+ $buffer->write('foo');
+
+ $this->assertTrue($buffer->isWritable());
+ $buffer->end();
+ $this->assertFalse($buffer->isWritable());
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::end
+ */
+ public function testEndWithDataClosesImmediatelyIfWritableResourceStreamFlushes()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $filterBuffer = '';
+ $loop = $this->createLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop);
+ $buffer->on('error', $this->expectCallableNever());
+ $buffer->on('close', $this->expectCallableOnce());
+
+ Filter\append($stream, function ($chunk) use (&$filterBuffer) {
+ $filterBuffer .= $chunk;
+ return $chunk;
+ });
+
+ $this->assertTrue($buffer->isWritable());
+ $buffer->end('final words');
+ $this->assertFalse($buffer->isWritable());
+
+ $buffer->handleWrite();
+ $this->assertSame('final words', $filterBuffer);
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::end
+ */
+ public function testEndWithDataDoesNotCloseImmediatelyIfWritableResourceStreamIsFull()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop);
+ $buffer->on('error', $this->expectCallableNever());
+ $buffer->on('close', $this->expectCallableNever());
+
+ $buffer->write('foo');
+
+ $this->assertTrue($buffer->isWritable());
+ $buffer->end('final words');
+ $this->assertFalse($buffer->isWritable());
+
+ rewind($stream);
+ $this->assertSame('', stream_get_contents($stream));
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::isWritable
+ * @covers React\Stream\WritableResourceStream::close
+ */
+ public function testClose()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop);
+ $buffer->on('error', $this->expectCallableNever());
+ $buffer->on('close', $this->expectCallableOnce());
+
+ $this->assertTrue($buffer->isWritable());
+ $buffer->close();
+ $this->assertFalse($buffer->isWritable());
+
+ $this->assertEquals(array(), $buffer->listeners('close'));
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::close
+ */
+ public function testClosingAfterWriteRemovesStreamFromLoop()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $buffer = new WritableResourceStream($stream, $loop);
+
+ $loop->expects($this->once())->method('removeWriteStream')->with($stream);
+
+ $buffer->write('foo');
+ $buffer->close();
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::close
+ */
+ public function testClosingWithoutWritingDoesNotRemoveStreamFromLoop()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $buffer = new WritableResourceStream($stream, $loop);
+
+ $loop->expects($this->never())->method('removeWriteStream');
+
+ $buffer->close();
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::close
+ */
+ public function testDoubleCloseWillEmitOnlyOnce()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop);
+ $buffer->on('close', $this->expectCallableOnce());
+
+ $buffer->close();
+ $buffer->close();
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::write
+ * @covers React\Stream\WritableResourceStream::close
+ */
+ public function testWritingToClosedWritableResourceStreamShouldNotWriteToStream()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $filterBuffer = '';
+ $loop = $this->createLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop);
+
+ Filter\append($stream, function ($chunk) use (&$filterBuffer) {
+ $filterBuffer .= $chunk;
+ return $chunk;
+ });
+
+ $buffer->close();
+
+ $buffer->write('foo');
+
+ $buffer->handleWrite();
+ $this->assertSame('', $filterBuffer);
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::handleWrite
+ */
+ public function testErrorWhenStreamResourceIsInvalid()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createWriteableLoopMock();
+
+ $error = null;
+
+ $buffer = new WritableResourceStream($stream, $loop);
+ $buffer->on('error', function ($message) use (&$error) {
+ $error = $message;
+ });
+
+ // invalidate stream resource
+ fclose($stream);
+
+ $buffer->write('Attempting to write to bad stream');
+
+ $this->assertInstanceOf('Exception', $error);
+
+ // the error messages differ between PHP versions, let's just check substrings
+ $this->assertContains('Unable to write to stream: ', $error->getMessage());
+ $this->assertContains(' not a valid stream resource', $error->getMessage(), '', true);
+ }
+
+ public function testWritingToClosedStream()
+ {
+ if ('Darwin' === PHP_OS) {
+ $this->markTestSkipped('OS X issue with shutting down pair for writing');
+ }
+
+ list($a, $b) = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
+ $loop = $this->createLoopMock();
+
+ $error = null;
+
+ $buffer = new WritableResourceStream($a, $loop);
+ $buffer->on('error', function($message) use (&$error) {
+ $error = $message;
+ });
+
+ $buffer->write('foo');
+ $buffer->handleWrite();
+ stream_socket_shutdown($b, STREAM_SHUT_RD);
+ stream_socket_shutdown($a, STREAM_SHUT_RD);
+ $buffer->write('bar');
+ $buffer->handleWrite();
+
+ $this->assertInstanceOf('Exception', $error);
+ $this->assertSame('Unable to write to stream: fwrite(): send of 3 bytes failed with errno=32 Broken pipe', $error->getMessage());
+ }
+
+ private function createWriteableLoopMock()
+ {
+ $loop = $this->createLoopMock();
+ $loop->preventWrites = false;
+ $loop
+ ->expects($this->any())
+ ->method('addWriteStream')
+ ->will($this->returnCallback(function ($stream, $listener) use ($loop) {
+ if (!$loop->preventWrites) {
+ call_user_func($listener, $stream);
+ }
+ }));
+
+ return $loop;
+ }
+
+ private function createLoopMock()
+ {
+ return $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ }
+}