pax_global_header00006660000000000000000000000064132171720750014517gustar00rootroot0000000000000052 comment=00e269d611e9c9a29356aef64c07f7e513e73dc9 promise-stream-1.1.1/000077500000000000000000000000001321717207500144665ustar00rootroot00000000000000promise-stream-1.1.1/.gitignore000066400000000000000000000000271321717207500164550ustar00rootroot00000000000000/vendor /composer.lock promise-stream-1.1.1/.travis.yml000066400000000000000000000006451321717207500166040ustar00rootroot00000000000000language: php php: # - 5.3 # requires old distro, see below - 5.4 - 5.5 - 5.6 - 7.0 - 7.1 - 7.2 - hhvm # ignore errors, see below # lock distro so new future defaults will not break the build dist: trusty matrix: include: - php: 5.3 dist: precise allow_failures: - php: hhvm sudo: false install: - composer install --no-interaction script: - ./vendor/bin/phpunit --coverage-text promise-stream-1.1.1/CHANGELOG.md000066400000000000000000000022411321717207500162760ustar00rootroot00000000000000# Changelog ## 1.1.1 (2017-12-22) * Fix: Fix `all()` to assume null values if no event data is passed (#13 by @clue) * Improve test suite by simplifying test bootstrapping logic via Composer and add forward compatibility with PHPUnit 5 and PHPUnit 6 and test against PHP 7.1 and 7.2 (#11 and #12 by @clue and #9 by @carusogabriel) ## 1.1.0 (2017-11-28) * Feature: Reject `first()` when stream emits an error event (#7 by @clue) * Fix: Explicit `close()` of unwrapped stream should not emit `error` event (#8 by @clue) * Internal refactoring to simplify `buffer()` function (#6 by @kelunik) ## 1.0.0 (2017-10-24) * First stable release, now following SemVer > Contains no other changes, so it's actually fully compatible with the v0.1.2 release. ## 0.1.2 (2017-10-18) * Feature: Optional maximum buffer length for `buffer()` (#3 by @WyriHaximus) * Improvement: Readme improvements (#5 by @jsor) ## 0.1.1 (2017-05-15) * Improvement: Forward compatibility with stream 1.0, 0.7, 0.6, and 0.5 (#2 by @WyriHaximus) ## 0.1.0 (2017-05-10) * Initial release, adapted from [`clue/promise-stream-react`](https://github.com/clue/php-promise-stream-react) promise-stream-1.1.1/LICENSE000066400000000000000000000020721321717207500154740ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2016 Christian Lück Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. promise-stream-1.1.1/README.md000066400000000000000000000172061321717207500157530ustar00rootroot00000000000000# PromiseStream [![Build Status](https://travis-ci.org/reactphp/promise-stream.svg?branch=master)](https://travis-ci.org/reactphp/promise-stream) The missing link between Promise-land and Stream-land for [ReactPHP](https://reactphp.org/). **Table of Contents** * [Usage](#usage) * [buffer()](#buffer) * [first()](#first) * [all()](#all) * [unwrapReadable()](#unwrapreadable) * [unwrapWritable()](#unwrapwritable) * [Install](#install) * [License](#license) ## Usage This lightweight library consists only of a few simple functions. All functions reside under the `React\Promise\Stream` namespace. The below examples assume you use an import statement similar to this: ```php use React\Promise\Stream; Stream\buffer(…); ``` Alternatively, you can also refer to them with their fully-qualified name: ```php \React\Promise\Stream\buffer(…); ``` ### buffer() The `buffer(ReadableStreamInterface $stream, int $maxLength = null)` function can be used to create a `Promise` which resolves with the stream data buffer. With an optional maximum length argument which defaults to no limit. In case the maximum length is reached before the end the promise will be rejected with a `\OverflowException`. ```php $stream = accessSomeJsonStream(); Stream\buffer($stream)->then(function ($contents) { var_dump(json_decode($contents)); }); ``` The promise will resolve with all data chunks concatenated once the stream closes. The promise will resolve with an empty string if the stream is already closed. The promise will reject if the stream emits an error. The promise will reject if it is canceled. ```php $stream = accessSomeToLargeStream(); Stream\buffer($stream, 1024)->then(function ($contents) { var_dump(json_decode($contents)); }, function ($error) { // Reaching here when the stream buffer goes above the max size, // in this example that is 1024 bytes, // or when the stream emits an error. }); ``` ### first() The `first(ReadableStreamInterface|WritableStreamInterface $stream, $event = 'data')` function can be used to create a `Promise` which resolves once the given event triggers for the first time. ```php $stream = accessSomeJsonStream(); Stream\first($stream)->then(function ($chunk) { echo 'The first chunk arrived: ' . $chunk; }); ``` The promise will resolve with whatever the first event emitted or `null` if the event does not pass any data. If you do not pass a custom event name, then it will wait for the first "data" event and resolve with a string containing the first data chunk. The promise will reject if the stream emits an error – unless you're waiting for the "error" event, in which case it will resolve. The promise will reject once the stream closes – unless you're waiting for the "close" event, in which case it will resolve. The promise will reject if the stream is already closed. The promise will reject if it is canceled. ### all() The `all(ReadableStreamInterface|WritableStreamInterface $stream, $event = 'data')` function can be used to create a `Promise` which resolves with an array of all the event data. ```php $stream = accessSomeJsonStream(); Stream\all($stream)->then(function ($chunks) { echo 'The stream consists of ' . count($chunks) . ' chunk(s)'; }); ``` The promise will resolve with an array of whatever all events emitted or `null` if the events do not pass any data. If you do not pass a custom event name, then it will wait for all the "data" events and resolve with an array containing all the data chunks. The promise will resolve with an array once the stream closes. The promise will resolve with an empty array if the stream is already closed. The promise will reject if the stream emits an error. The promise will reject if it is canceled. ### unwrapReadable() The `unwrapReadable(PromiseInterface $promise)` function can be used to unwrap a `Promise` which resolves with a `ReadableStreamInterface`. This function returns a readable stream instance (implementing `ReadableStreamInterface`) right away which acts as a proxy for the future promise resolution. Once the given Promise resolves with a `ReadableStreamInterface`, its data will be piped to the output stream. ```php //$promise = someFunctionWhichResolvesWithAStream(); $promise = startDownloadStream($uri); $stream = Stream\unwrapReadable($promise); $stream->on('data', function ($data) { echo $data; }); $stream->on('end', function () { echo 'DONE'; }); ``` If the given promise is either rejected or fulfilled with anything but an instance of `ReadableStreamInterface`, then the output stream will emit an `error` event and close: ```php $promise = startDownloadStream($invalidUri); $stream = Stream\unwrapReadable($promise); $stream->on('error', function (Exception $error) { echo 'Error: ' . $error->getMessage(); }); ``` The given `$promise` SHOULD be pending, i.e. it SHOULD NOT be fulfilled or rejected at the time of invoking this function. If the given promise is already settled and does not resolve with an instance of `ReadableStreamInterface`, then you will not be able to receive the `error` event. You can `close()` the resulting stream at any time, which will either try to `cancel()` the pending promise or try to `close()` the underlying stream. ```php $promise = startDownloadStream($uri); $stream = Stream\unwrapReadable($promise); $loop->addTimer(2.0, function () use ($stream) { $stream->close(); }); ``` ### unwrapWritable() The `unwrapWritable(PromiseInterface $promise)` function can be used to unwrap a `Promise` which resolves with a `WritableStreamInterface`. This function returns a writable stream instance (implementing `WritableStreamInterface`) right away which acts as a proxy for the future promise resolution. Once the given Promise resolves with a `WritableStreamInterface`, any data you wrote to the proxy will be piped to the inner stream. ```php //$promise = someFunctionWhichResolvesWithAStream(); $promise = startUploadStream($uri); $stream = Stream\unwrapWritable($promise); $stream->write('hello'); $stream->end('world'); $stream->on('close', function () { echo 'DONE'; }); ``` If the given promise is either rejected or fulfilled with anything but an instance of `WritableStreamInterface`, then the output stream will emit an `error` event and close: ```php $promise = startUploadStream($invalidUri); $stream = Stream\unwrapWritable($promise); $stream->on('error', function (Exception $error) { echo 'Error: ' . $error->getMessage(); }); ``` The given `$promise` SHOULD be pending, i.e. it SHOULD NOT be fulfilled or rejected at the time of invoking this function. If the given promise is already settled and does not resolve with an instance of `WritableStreamInterface`, then you will not be able to receive the `error` event. You can `close()` the resulting stream at any time, which will either try to `cancel()` the pending promise or try to `close()` the underlying stream. ```php $promise = startUploadStream($uri); $stream = Stream\unwrapWritable($promise); $loop->addTimer(2.0, function () use ($stream) { $stream->close(); }); ``` ## Install The recommended way to install this library is [through Composer](https://getcomposer.org). [New to Composer?](https://getcomposer.org/doc/00-intro.md) This project follows [SemVer](http://semver.org/). This will install the latest supported version: ```bash $ composer require react/promise-stream:^1.1.1 ``` See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades. This project aims to run on any platform and thus does not require any PHP extensions and supports running on legacy PHP 5.3 through current PHP 7+ and HHVM. It's *highly recommended to use PHP 7+* for this project. ## License MIT, see [LICENSE file](LICENSE). promise-stream-1.1.1/composer.json000066400000000000000000000017731321717207500172200ustar00rootroot00000000000000{ "name": "react/promise-stream", "description": "The missing link between Promise-land and Stream-land for ReactPHP", "keywords": ["unwrap", "stream", "buffer", "promise", "ReactPHP", "async"], "homepage": "https://github.com/reactphp/promise-stream", "license": "MIT", "authors": [ { "name": "Christian Lück", "email": "christian@lueck.tv" } ], "autoload": { "psr-4": { "React\\Promise\\Stream\\" : "src/" }, "files": [ "src/functions_include.php" ] }, "autoload-dev": { "psr-4": { "React\\Tests\\Promise\\Stream\\": "tests/" } }, "require": { "php": ">=5.3", "react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4 || ^0.3", "react/promise": "^2.1 || ^1.2" }, "require-dev": { "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3", "react/promise-timer": "^1.0", "clue/block-react": "^1.0", "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" } } promise-stream-1.1.1/phpunit.xml.dist000066400000000000000000000007331321717207500176440ustar00rootroot00000000000000 ./tests/ ./src/ promise-stream-1.1.1/src/000077500000000000000000000000001321717207500152555ustar00rootroot00000000000000promise-stream-1.1.1/src/UnwrapReadableStream.php000066400000000000000000000072271321717207500220460ustar00rootroot00000000000000 */ public function __construct(PromiseInterface $promise) { $out = $this; $closed =& $this->closed; $this->promise = $promise->then( function ($stream) { if (!($stream instanceof ReadableStreamInterface)) { throw new InvalidArgumentException('Not a readable stream'); } return $stream; } )->then( function (ReadableStreamInterface $stream) use ($out, &$closed) { // stream is already closed, make sure to close output stream if (!$stream->isReadable()) { $out->close(); return $stream; } // resolves but output is already closed, make sure to close stream silently if ($closed) { $stream->close(); return $stream; } // stream any writes into output stream $stream->on('data', function ($data) use ($out) { $out->emit('data', array($data, $out)); }); // forward end events and close $stream->on('end', function () use ($out, &$closed) { if (!$closed) { $out->emit('end', array($out)); $out->close(); } }); // error events cancel output stream $stream->on('error', function ($error) use ($out) { $out->emit('error', array($error, $out)); $out->close(); }); // close both streams once either side closes $stream->on('close', array($out, 'close')); $out->on('close', array($stream, 'close')); return $stream; }, function ($e) use ($out, &$closed) { if (!$closed) { $out->emit('error', array($e, $out)); $out->close(); } } ); } public function isReadable() { return !$this->closed; } public function pause() { $this->promise->then(function (ReadableStreamInterface $stream) { $stream->pause(); }); } public function resume() { $this->promise->then(function (ReadableStreamInterface $stream) { $stream->resume(); }); } public function pipe(WritableStreamInterface $dest, array $options = array()) { Util::pipe($this, $dest, $options); return $dest; } public function close() { if ($this->closed) { return; } $this->closed = true; // try to cancel promise once the stream closes if ($this->promise instanceof CancellablePromiseInterface) { $this->promise->cancel(); } $this->emit('close', array($this)); } } promise-stream-1.1.1/src/UnwrapWritableStream.php000066400000000000000000000106521321717207500221140ustar00rootroot00000000000000 */ public function __construct(PromiseInterface $promise) { $out = $this; $store =& $this->stream; $buffer =& $this->buffer; $ending =& $this->ending; $closed =& $this->closed; $this->promise = $promise->then( function ($stream) { if (!($stream instanceof WritableStreamInterface)) { throw new InvalidArgumentException('Not a writable stream'); } return $stream; } )->then( function (WritableStreamInterface $stream) use ($out, &$store, &$buffer, &$ending, &$closed) { // stream is already closed, make sure to close output stream if (!$stream->isWritable()) { $out->close(); return $stream; } // resolves but output is already closed, make sure to close stream silently if ($closed) { $stream->close(); return $stream; } // forward drain events for back pressure $stream->on('drain', function () use ($out) { $out->emit('drain', array($out)); }); // error events cancel output stream $stream->on('error', function ($error) use ($out) { $out->emit('error', array($error, $out)); $out->close(); }); // close both streams once either side closes $stream->on('close', array($out, 'close')); $out->on('close', array($stream, 'close')); if ($buffer !== '') { // flush buffer to stream and check if its buffer is not exceeded $drained = $stream->write($buffer) !== false; $buffer = ''; if ($drained) { // signal drain event, because the output stream previous signalled a full buffer $out->emit('drain', array($out)); } } if ($ending) { $stream->end(); } else { $store = $stream; } return $stream; }, function ($e) use ($out, &$closed) { if (!$closed) { $out->emit('error', array($e, $out)); $out->close(); } } ); } public function write($data) { if ($this->ending) { return; } // forward to inner stream if possible if ($this->stream !== null) { return $this->stream->write($data); } // append to buffer and signal the buffer is full $this->buffer .= $data; return false; } public function end($data = null) { if ($this->ending) { return; } $this->ending = true; // forward to inner stream if possible if ($this->stream !== null) { return $this->stream->end($data); } // append to buffer if ($data !== null) { $this->buffer .= $data; } } public function isWritable() { return !$this->ending; } public function close() { if ($this->closed) { return; } $this->buffer = ''; $this->ending = true; $this->closed = true; // try to cancel promise once the stream closes if ($this->promise instanceof CancellablePromiseInterface) { $this->promise->cancel(); } $this->emit('close', array($this)); } } promise-stream-1.1.1/src/functions.php000066400000000000000000000142531321717207500200030ustar00rootroot00000000000000 */ function buffer(ReadableStreamInterface $stream, $maxLength = null) { // stream already ended => resolve with empty buffer if (!$stream->isReadable()) { return Promise\resolve(''); } $buffer = ''; $promise = new Promise\Promise(function ($resolve, $reject) use ($stream, $maxLength, &$buffer, &$bufferer) { $bufferer = function ($data) use (&$buffer, $reject, $maxLength) { $buffer .= $data; if ($maxLength !== null && isset($buffer[$maxLength])) { $reject(new \OverflowException('Buffer exceeded maximum length')); } }; $stream->on('data', $bufferer); $stream->on('error', function ($error) use ($reject) { $reject(new \RuntimeException('An error occured on the underlying stream while buffering', 0, $error)); }); $stream->on('close', function () use ($resolve, &$buffer) { $resolve($buffer); }); }, function ($_, $reject) { $reject(new \RuntimeException('Cancelled buffering')); }); return $promise->then(null, function ($error) use (&$buffer, $bufferer, $stream) { // promise rejected => clear buffer and buffering $buffer = ''; $stream->removeListener('data', $bufferer); throw $error; }); } /** * Creates a `Promise` which resolves with the first event data * * @param ReadableStreamInterface|WritableStreamInterface $stream * @param string $event * @return Promise\CancellablePromiseInterface Promise */ function first(EventEmitterInterface $stream, $event = 'data') { if ($stream instanceof ReadableStreamInterface) { // readable or duplex stream not readable => already closed // a half-open duplex stream is considered closed if its readable side is closed if (!$stream->isReadable()) { return Promise\reject(new \RuntimeException('Stream already closed')); } } elseif ($stream instanceof WritableStreamInterface) { // writable-only stream (not duplex) not writable => already closed if (!$stream->isWritable()) { return Promise\reject(new \RuntimeException('Stream already closed')); } } return new Promise\Promise(function ($resolve, $reject) use ($stream, $event, &$listener) { $listener = function ($data = null) use ($stream, $event, &$listener, $resolve) { $stream->removeListener($event, $listener); $resolve($data); }; $stream->on($event, $listener); if ($event !== 'error') { $stream->on('error', function ($error) use ($stream, $event, $listener, $reject) { $stream->removeListener($event, $listener); $reject(new \RuntimeException('An error occured on the underlying stream while waiting for event', 0, $error)); }); } $stream->on('close', function () use ($stream, $event, $listener, $reject) { $stream->removeListener($event, $listener); $reject(new \RuntimeException('Stream closed')); }); }, function ($_, $reject) use ($stream, $event, &$listener) { $stream->removeListener($event, $listener); $reject(new \RuntimeException('Operation cancelled')); }); } /** * Creates a `Promise` which resolves with an array of all the event data * * @param ReadableStreamInterface|WritableStreamInterface $stream * @param string $event * @return Promise\CancellablePromiseInterface Promise */ function all(EventEmitterInterface $stream, $event = 'data') { // stream already ended => resolve with empty buffer if ($stream instanceof ReadableStreamInterface) { // readable or duplex stream not readable => already closed // a half-open duplex stream is considered closed if its readable side is closed if (!$stream->isReadable()) { return Promise\resolve(array()); } } elseif ($stream instanceof WritableStreamInterface) { // writable-only stream (not duplex) not writable => already closed if (!$stream->isWritable()) { return Promise\resolve(array()); } } $buffer = array(); $bufferer = function ($data = null) use (&$buffer) { $buffer []= $data; }; $stream->on($event, $bufferer); $promise = new Promise\Promise(function ($resolve, $reject) use ($stream, &$buffer) { $stream->on('error', function ($error) use ($reject) { $reject(new \RuntimeException('An error occured on the underlying stream while buffering', 0, $error)); }); $stream->on('close', function () use ($resolve, &$buffer) { $resolve($buffer); }); }, function ($_, $reject) { $reject(new \RuntimeException('Cancelled buffering')); }); return $promise->then(null, function ($error) use (&$buffer, $bufferer, $stream, $event) { // promise rejected => clear buffer and buffering $buffer = array(); $stream->removeListener($event, $bufferer); throw $error; }); } /** * unwrap a `Promise` which resolves with a `ReadableStreamInterface`. * * @param PromiseInterface $promise Promise * @return ReadableStreamInterface */ function unwrapReadable(PromiseInterface $promise) { return new UnwrapReadableStream($promise); } /** * unwrap a `Promise` which resolves with a `WritableStreamInterface`. * * @param PromiseInterface $promise Promise * @return WritableStreamInterface */ function unwrapWritable(PromiseInterface $promise) { return new UnwrapWritableStream($promise); } promise-stream-1.1.1/src/functions_include.php000066400000000000000000000002111321717207500214730ustar00rootroot00000000000000close(); $promise = Stream\all($stream); $this->expectPromiseResolveWith(array(), $promise); } public function testClosedWritableStreamResolvesWithEmptyBuffer() { $stream = new ThroughStream(); $stream->close(); $promise = Stream\all($stream); $this->expectPromiseResolveWith(array(), $promise); } public function testPendingStreamWillNotResolve() { $stream = new ThroughStream(); $promise = Stream\all($stream); $promise->then($this->expectCallableNever(), $this->expectCallableNever()); } public function testClosingStreamResolvesWithEmptyBuffer() { $stream = new ThroughStream(); $promise = Stream\all($stream); $stream->close(); $this->expectPromiseResolveWith(array(), $promise); } public function testClosingWritableStreamResolvesWithEmptyBuffer() { $stream = new ThroughStream(); $promise = Stream\all($stream); $stream->close(); $this->expectPromiseResolveWith(array(), $promise); } public function testEmittingDataOnStreamResolvesWithArrayOfData() { $stream = new ThroughStream(); $promise = Stream\all($stream); $stream->emit('data', array('hello', $stream)); $stream->emit('data', array('world', $stream)); $stream->close(); $this->expectPromiseResolveWith(array('hello', 'world'), $promise); } public function testEmittingCustomEventOnStreamResolvesWithArrayOfCustomEventData() { $stream = new ThroughStream(); $promise = Stream\all($stream, 'a'); $stream->emit('a', array('hello')); $stream->emit('b', array('ignored')); $stream->emit('a'); $stream->close(); $this->expectPromiseResolveWith(array('hello', null), $promise); } public function testEmittingErrorOnStreamRejects() { $stream = new ThroughStream(); $promise = Stream\all($stream); $stream->emit('error', array(new \RuntimeException('test'))); $this->expectPromiseReject($promise); } public function testEmittingErrorAfterEmittingDataOnStreamRejects() { $stream = new ThroughStream(); $promise = Stream\all($stream); $stream->emit('data', array('hello', $stream)); $stream->emit('error', array(new \RuntimeException('test'))); $this->expectPromiseReject($promise); } public function testCancelPendingStreamWillReject() { $stream = new ThroughStream(); $promise = Stream\all($stream); $promise->cancel(); $this->expectPromiseReject($promise); } } promise-stream-1.1.1/tests/BufferTest.php000066400000000000000000000062021321717207500204120ustar00rootroot00000000000000close(); $promise = Stream\buffer($stream); $this->expectPromiseResolveWith('', $promise); } public function testPendingStreamWillNotResolve() { $stream = new ThroughStream(); $promise = Stream\buffer($stream); $promise->then($this->expectCallableNever(), $this->expectCallableNever()); } public function testClosingStreamResolvesWithEmptyBuffer() { $stream = new ThroughStream(); $promise = Stream\buffer($stream); $stream->close(); $this->expectPromiseResolveWith('', $promise); } public function testEmittingDataOnStreamResolvesWithConcatenatedData() { $stream = new ThroughStream(); $promise = Stream\buffer($stream); $stream->emit('data', array('hello', $stream)); $stream->emit('data', array('world', $stream)); $stream->close(); $this->expectPromiseResolveWith('helloworld', $promise); } public function testEmittingErrorOnStreamRejects() { $stream = new ThroughStream(); $promise = Stream\buffer($stream); $stream->emit('error', array(new \RuntimeException('test'))); $this->expectPromiseReject($promise); } public function testEmittingErrorAfterEmittingDataOnStreamRejects() { $stream = new ThroughStream(); $promise = Stream\buffer($stream); $stream->emit('data', array('hello', $stream)); $stream->emit('error', array(new \RuntimeException('test'))); $this->expectPromiseReject($promise); } public function testCancelPendingStreamWillReject() { $stream = new ThroughStream(); $promise = Stream\buffer($stream); $promise->cancel(); $this->expectPromiseReject($promise); } public function testMaximumSize() { $loop = Factory::create(); $stream = new ThroughStream(); $loop->addTimer(0.1, function () use ($stream) { $stream->write('12345678910111213141516'); }); $promise = Stream\buffer($stream, 16); if (method_exists($this, 'expectException')) { $this->expectException('OverflowException'); $this->expectExceptionMessage('Buffer exceeded maximum length'); } else { $this->setExpectedException('\OverflowException', 'Buffer exceeded maximum length'); } Block\await($promise, $loop, 10); } public function testUnderMaximumSize() { $loop = Factory::create(); $stream = new ThroughStream(); $loop->addTimer(0.1, function () use ($stream) { $stream->write('1234567891011'); $stream->end(); }); $promise = Stream\buffer($stream, 16); $result = Block\await($promise, $loop, 10); $this->assertSame('1234567891011', $result); } } promise-stream-1.1.1/tests/CallableStub.php000066400000000000000000000001601321717207500206730ustar00rootroot00000000000000close(); $promise = Stream\first($stream); $this->expectPromiseReject($promise); } public function testClosedWritableStreamRejects() { $stream = new ThroughStream(); $stream->close(); $promise = Stream\first($stream); $this->expectPromiseReject($promise); } public function testPendingStreamWillNotResolve() { $stream = new ThroughStream(); $promise = Stream\first($stream); $promise->then($this->expectCallableNever(), $this->expectCallableNever()); } public function testClosingStreamRejects() { $stream = new ThroughStream(); $promise = Stream\first($stream); $stream->close(); $this->expectPromiseReject($promise); } public function testClosingWritableStreamRejects() { $stream = new ThroughStream(); $promise = Stream\first($stream); $stream->close(); $this->expectPromiseReject($promise); } public function testClosingStreamResolvesWhenWaitingForCloseEvent() { $stream = new ThroughStream(); $promise = Stream\first($stream, 'close'); $stream->close(); $this->expectPromiseResolve($promise); } public function testEmittingDataOnStreamResolvesWithFirstEvent() { $stream = new ThroughStream(); $promise = Stream\first($stream); $stream->emit('data', array('hello', $stream)); $stream->emit('data', array('world', $stream)); $stream->close(); $this->expectPromiseResolveWith('hello', $promise); } public function testEmittingErrorOnStreamWillReject() { $stream = new ThroughStream(); $promise = Stream\first($stream); $stream->emit('error', array(new \RuntimeException('test'))); $this->expectPromiseReject($promise); } public function testEmittingErrorResolvesWhenWaitingForErrorEvent() { $stream = new ThroughStream(); $promise = Stream\first($stream, 'error'); $stream->emit('error', array(new \RuntimeException('test'))); $this->expectPromiseResolve($promise); } public function testCancelPendingStreamWillReject() { $stream = new ThroughStream(); $promise = Stream\first($stream); $promise->cancel(); $this->expectPromiseReject($promise); } } promise-stream-1.1.1/tests/TestCase.php000066400000000000000000000041551321717207500200610ustar00rootroot00000000000000createCallableMock(); $mock ->expects($this->once()) ->method('__invoke'); return $mock; } protected function expectCallableOnceWith($value) { $mock = $this->createCallableMock(); $mock ->expects($this->once()) ->method('__invoke') ->with($this->equalTo($value)); return $mock; } protected function expectCallableOnceParameter($type) { $mock = $this->createCallableMock(); $mock ->expects($this->once()) ->method('__invoke') ->with($this->isInstanceOf($type)); return $mock; } protected function expectCallableNever() { $mock = $this->createCallableMock(); $mock ->expects($this->never()) ->method('__invoke'); return $mock; } /** * @link https://github.com/reactphp/react/blob/master/tests/React/Tests/Socket/TestCase.php (taken from reactphp/react) */ protected function createCallableMock() { return $this->getMockBuilder('React\Tests\Promise\Stream\CallableStub')->getMock(); } protected function expectPromiseResolve($promise) { $this->assertInstanceOf('React\Promise\PromiseInterface', $promise); $promise->then($this->expectCallableOnce(), $this->expectCallableNever()); return $promise; } protected function expectPromiseResolveWith($with, $promise) { $this->assertInstanceOf('React\Promise\PromiseInterface', $promise); $promise->then($this->expectCallableOnce($with), $this->expectCallableNever()); return $promise; } protected function expectPromiseReject($promise) { $this->assertInstanceOf('React\Promise\PromiseInterface', $promise); $promise->then($this->expectCallableNever(), $this->expectCallableOnce()); return $promise; } } promise-stream-1.1.1/tests/UnwrapReadableTest.php000066400000000000000000000175331321717207500221060ustar00rootroot00000000000000loop = Factory::create(); } public function testReturnsReadableStreamForPromise() { $promise = new \React\Promise\Promise(function () { }); $stream = Stream\unwrapReadable($promise); $this->assertTrue($stream->isReadable()); } public function testClosingStreamMakesItNotReadable() { $promise = new \React\Promise\Promise(function () { }); $stream = Stream\unwrapReadable($promise); $stream->on('close', $this->expectCallableOnce()); $stream->on('end', $this->expectCallableNever()); $stream->on('error', $this->expectCallableNever()); $stream->close(); $this->assertFalse($stream->isReadable()); } public function testClosingRejectingStreamMakesItNotReadable() { $promise = Timer\reject(0.001, $this->loop); $stream = Stream\unwrapReadable($promise); $stream->on('close', $this->expectCallableOnce()); $stream->on('end', $this->expectCallableNever()); $stream->on('error', $this->expectCallableNever()); $stream->close(); $this->loop->run(); $this->assertFalse($stream->isReadable()); } public function testClosingStreamWillCancelInputPromiseAndMakeStreamNotReadable() { $promise = new \React\Promise\Promise(function () { }, $this->expectCallableOnce()); $stream = Stream\unwrapReadable($promise); $stream->close(); $this->assertFalse($stream->isReadable()); } public function testEmitsErrorWhenPromiseRejects() { $promise = Timer\reject(0.001, $this->loop); $stream = Stream\unwrapReadable($promise); $this->assertTrue($stream->isReadable()); $stream->on('error', $this->expectCallableOnce()); $stream->on('end', $this->expectCallableNever()); $this->loop->run(); $this->assertFalse($stream->isReadable()); } public function testEmitsErrorWhenPromiseResolvesWithWrongValue() { $promise = Timer\resolve(0.001, $this->loop); $stream = Stream\unwrapReadable($promise); $this->assertTrue($stream->isReadable()); $stream->on('error', $this->expectCallableOnce()); $stream->on('end', $this->expectCallableNever()); $this->loop->run(); $this->assertFalse($stream->isReadable()); } public function testReturnsClosedStreamIfInputStreamIsClosed() { $input = new ThroughStream(); $input->close(); $promise = Promise\resolve($input); $stream = Stream\unwrapReadable($promise); $this->assertFalse($stream->isReadable()); } public function testReturnsClosedStreamIfInputHasWrongValue() { $promise = Promise\resolve(42); $stream = Stream\unwrapReadable($promise); $this->assertFalse($stream->isReadable()); } public function testReturnsStreamThatWillBeClosedWhenPromiseResolvesWithClosedInputStream() { $input = new ThroughStream(); $input->close(); $promise = Timer\resolve(0.001, $this->loop)->then(function () use ($input) { return $input; }); $stream = Stream\unwrapReadable($promise); $this->assertTrue($stream->isReadable()); $stream->on('close', $this->expectCallableOnce()); $this->loop->run(); $this->assertFalse($stream->isReadable()); } public function testEmitsDataWhenInputEmitsData() { $input = new ThroughStream(); $promise = Promise\resolve($input); $stream = Stream\unwrapReadable($promise); $stream->on('data', $this->expectCallableOnceWith('hello world')); $input->emit('data', array('hello world')); } public function testEmitsErrorAndClosesWhenInputEmitsError() { $input = new ThroughStream(); $promise = Promise\resolve($input); $stream = Stream\unwrapReadable($promise); $stream->on('error', $this->expectCallableOnceWith(new \RuntimeException())); $stream->on('close', $this->expectCallableOnce()); $input->emit('error', array(new \RuntimeException())); $this->assertFalse($stream->isReadable()); } public function testEmitsEndAndClosesWhenInputEmitsEnd() { $input = new ThroughStream(); $promise = Promise\resolve($input); $stream = Stream\unwrapReadable($promise); $stream->on('end', $this->expectCallableOnce()); $stream->on('close', $this->expectCallableOnce()); $input->emit('end', array()); $this->assertFalse($stream->isReadable()); } public function testEmitsCloseOnlyOnceWhenClosingStreamMultipleTimes() { $promise = new Promise\Promise(function () { }); $stream = Stream\unwrapReadable($promise); $stream->on('end', $this->expectCallableNever()); $stream->on('close', $this->expectCallableOnce()); $stream->close(); $stream->close(); } public function testForwardsPauseToInputStream() { $input = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock(); $input->expects($this->once())->method('pause'); $promise = Promise\resolve($input); $stream = Stream\unwrapReadable($promise); $stream->pause(); } public function testForwardsResumeToInputStream() { $input = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock(); $input->expects($this->once())->method('resume'); $promise = Promise\resolve($input); $stream = Stream\unwrapReadable($promise); $stream->resume(); } public function testPipingStreamWillForwardDataEvents() { $input = new ThroughStream(); $promise = Promise\resolve($input); $stream = Stream\unwrapReadable($promise); $output = new ThroughStream(); $outputPromise = Stream\buffer($output); $stream->pipe($output); $input->emit('data', array('hello')); $input->emit('data', array('world')); $input->end(); $outputPromise->then($this->expectCallableOnceWith('helloworld')); } public function testClosingStreamWillCloseInputStream() { $input = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock(); $input->expects($this->once())->method('isReadable')->willReturn(true); $input->expects($this->once())->method('close'); $promise = Promise\resolve($input); $stream = Stream\unwrapReadable($promise); $stream->close(); } public function testClosingStreamWillCloseStreamIfItIgnoredCancellationAndResolvesLater() { $input = new ThroughStream(); $loop = $this->loop; $promise = new Promise\Promise(function ($resolve) use ($loop, $input) { $loop->addTimer(0.001, function () use ($resolve, $input) { $resolve($input); }); }); $stream = Stream\unwrapReadable($promise); $stream->on('close', $this->expectCallableOnce()); $stream->close(); Block\await($promise, $this->loop); $this->assertFalse($input->isReadable()); } public function testClosingStreamWillCloseStreamFromCancellationHandler() { $input = new ThroughStream(); $promise = new \React\Promise\Promise(function () { }, function ($resolve) use ($input) { $resolve($input); }); $stream = Stream\unwrapReadable($promise); $stream->on('close', $this->expectCallableOnce()); $stream->close(); $this->assertFalse($input->isReadable()); } } promise-stream-1.1.1/tests/UnwrapWritableTest.php000066400000000000000000000233111321717207500221470ustar00rootroot00000000000000loop = Factory::create(); } public function testReturnsWritableStreamForPromise() { $promise = new \React\Promise\Promise(function () { }); $stream = Stream\unwrapWritable($promise); $this->assertTrue($stream->isWritable()); } public function testClosingStreamMakesItNotWritable() { $promise = new \React\Promise\Promise(function () { }); $stream = Stream\unwrapWritable($promise); $stream->on('close', $this->expectCallableOnce()); $stream->on('error', $this->expectCallableNever()); $stream->close(); $this->assertFalse($stream->isWritable()); } public function testClosingRejectingStreamMakesItNotWritable() { $promise = Timer\reject(0.001, $this->loop); $stream = Stream\unwrapWritable($promise); $stream->on('close', $this->expectCallableOnce()); $stream->on('error', $this->expectCallableNever()); $stream->close(); $this->loop->run(); $this->assertFalse($stream->isWritable()); } public function testClosingStreamWillCancelInputPromiseAndMakeStreamNotWritable() { $promise = new \React\Promise\Promise(function () { }, $this->expectCallableOnce()); $stream = Stream\unwrapWritable($promise); $stream->close(); $this->assertFalse($stream->isWritable()); } public function testEmitsErrorWhenPromiseRejects() { $promise = Timer\reject(0.001, $this->loop); $stream = Stream\unwrapWritable($promise); $this->assertTrue($stream->isWritable()); $stream->on('error', $this->expectCallableOnce()); $stream->on('close', $this->expectCallableOnce()); $this->loop->run(); $this->assertFalse($stream->isWritable()); } public function testEmitsErrorWhenPromiseResolvesWithWrongValue() { $promise = Timer\resolve(0.001, $this->loop); $stream = Stream\unwrapWritable($promise); $this->assertTrue($stream->isWritable()); $stream->on('error', $this->expectCallableOnce()); $stream->on('close', $this->expectCallableOnce()); $this->loop->run(); $this->assertFalse($stream->isWritable()); } public function testReturnsClosedStreamIfInputStreamIsClosed() { $input = new ThroughStream(); $input->close(); $promise = Promise\resolve($input); $stream = Stream\unwrapWritable($promise); $this->assertFalse($stream->isWritable()); } public function testReturnsClosedStreamIfInputHasWrongValue() { $promise = Promise\resolve(42); $stream = Stream\unwrapWritable($promise); $this->assertFalse($stream->isWritable()); } public function testReturnsStreamThatWillBeClosedWhenPromiseResolvesWithClosedInputStream() { $input = new ThroughStream(); $input->close(); $promise = Timer\resolve(0.001, $this->loop)->then(function () use ($input) { return $input; }); $stream = Stream\unwrapWritable($promise); $this->assertTrue($stream->isWritable()); $stream->on('close', $this->expectCallableOnce()); $this->loop->run(); $this->assertFalse($stream->isWritable()); } public function testForwardsDataImmediatelyIfPromiseIsAlreadyResolved() { $input = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock(); $input->expects($this->once())->method('isWritable')->willReturn(true); $input->expects($this->once())->method('write')->with('hello'); $input->expects($this->never())->method('end'); $promise = Promise\resolve($input); $stream = Stream\unwrapWritable($promise); $stream->write('hello'); } public function testForwardsDataInOneGoOncePromiseResolves() { $input = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock(); $input->expects($this->once())->method('isWritable')->willReturn(true); $input->expects($this->once())->method('write')->with('helloworld'); $input->expects($this->never())->method('end'); $promise = Timer\resolve(0.001, $this->loop)->then(function () use ($input) { return $input; }); $stream = Stream\unwrapWritable($promise); $stream->write('hello'); $stream->write('world'); $this->loop->run(); } public function testForwardsDataAndEndImmediatelyIfPromiseIsAlreadyResolved() { $input = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock(); $input->expects($this->once())->method('isWritable')->willReturn(true); $input->expects($this->once())->method('write')->with('hello'); $input->expects($this->once())->method('end')->with('!'); $promise = Promise\resolve($input); $stream = Stream\unwrapWritable($promise); $stream->write('hello'); $stream->end('!'); } public function testForwardsDataAndEndOncePromiseResolves() { $input = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock(); $input->expects($this->once())->method('isWritable')->willReturn(true); $input->expects($this->once())->method('write')->with('helloworld!'); $input->expects($this->once())->method('end'); $promise = Timer\resolve(0.001, $this->loop)->then(function () use ($input) { return $input; }); $stream = Stream\unwrapWritable($promise); $stream->write('hello'); $stream->write('world'); $stream->end('!'); $this->loop->run(); } public function testForwardsNoDataWhenWritingAfterEndIfPromiseIsAlreadyResolved() { $input = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock(); $input->expects($this->once())->method('isWritable')->willReturn(true); $input->expects($this->never())->method('write'); $input->expects($this->once())->method('end'); $promise = Promise\resolve($input); $stream = Stream\unwrapWritable($promise); $stream->end(); $stream->end(); $stream->write('nope'); } public function testForwardsNoDataWhenWritingAfterEndOncePromiseResolves() { $input = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock(); $input->expects($this->once())->method('isWritable')->willReturn(true); $input->expects($this->never())->method('write'); $input->expects($this->once())->method('end'); $promise = Timer\resolve(0.001, $this->loop)->then(function () use ($input) { return $input; }); $stream = Stream\unwrapWritable($promise); $stream->end(); $stream->write('nope'); $this->loop->run(); } public function testEmitsErrorAndClosesWhenInputEmitsError() { $input = new ThroughStream(); $promise = Promise\resolve($input); $stream = Stream\unwrapWritable($promise); $stream->on('error', $this->expectCallableOnceWith(new \RuntimeException())); $stream->on('close', $this->expectCallableOnce()); $input->emit('error', array(new \RuntimeException())); $this->assertFalse($stream->isWritable()); } public function testEmitsDrainWhenInputEmitsDrain() { $input = new ThroughStream(); $promise = Promise\resolve($input); $stream = Stream\unwrapWritable($promise); $stream->on('drain', $this->expectCallableOnce()); $input->emit('drain', array()); } public function testEmitsCloseOnlyOnceWhenClosingStreamMultipleTimes() { $promise = new Promise\Promise(function () { }); $stream = Stream\unwrapWritable($promise); $stream->on('close', $this->expectCallableOnce()); $stream->on('error', $this->expectCallableNever()); $stream->close(); $stream->close(); } public function testClosingStreamWillCloseInputStream() { $input = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock(); $input->expects($this->once())->method('isWritable')->willReturn(true); $input->expects($this->once())->method('close'); $promise = Promise\resolve($input); $stream = Stream\unwrapWritable($promise); $stream->close(); } public function testClosingStreamWillCloseStreamIfItIgnoredCancellationAndResolvesLater() { $this->markTestIncomplete(); $input = new ThroughStream(); $loop = $this->loop; $promise = new Promise\Promise(function ($resolve) use ($loop, $input) { $loop->addTimer(0.001, function () use ($resolve, $input) { $resolve($input); }); }); $stream = Stream\unwrapReadable($promise); $stream->on('close', $this->expectCallableOnce()); $stream->close(); Block\await($promise, $this->loop); $this->assertFalse($input->isReadable()); } public function testClosingStreamWillCloseStreamFromCancellationHandler() { $input = new ThroughStream(); $promise = new \React\Promise\Promise(function () { }, function ($resolve) use ($input) { $resolve($input); }); $stream = Stream\unwrapWritable($promise); $stream->on('close', $this->expectCallableOnce()); $stream->close(); $this->assertFalse($input->isWritable()); } }