You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
256 lines
6.6 KiB
256 lines
6.6 KiB
<?php |
|
|
|
namespace React\Tests\Stream; |
|
|
|
use React\Stream\Stream; |
|
use Clue\StreamFilter as Filter; |
|
|
|
class StreamTest extends TestCase |
|
{ |
|
/** |
|
* @covers React\Stream\Stream::__construct |
|
*/ |
|
public function testConstructor() |
|
{ |
|
$stream = fopen('php://temp', 'r+'); |
|
$loop = $this->createLoopMock(); |
|
|
|
$conn = new Stream($stream, $loop); |
|
} |
|
|
|
/** |
|
* @covers React\Stream\Stream::__construct |
|
*/ |
|
public function testConstructorThrowsExceptionOnInvalidStream() |
|
{ |
|
$this->setExpectedException('InvalidArgumentException'); |
|
$loop = $this->createLoopMock(); |
|
$conn = new Stream('breakme', $loop); |
|
} |
|
|
|
/** |
|
* @covers React\Stream\Stream::__construct |
|
* @covers React\Stream\Stream::handleData |
|
*/ |
|
public function testDataEvent() |
|
{ |
|
$stream = fopen('php://temp', 'r+'); |
|
$loop = $this->createLoopMock(); |
|
|
|
$capturedData = null; |
|
|
|
$conn = new Stream($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\Stream::__construct |
|
* @covers React\Stream\Stream::handleData |
|
*/ |
|
public function testDataEventDoesEmitOneChunkMatchingBufferSize() |
|
{ |
|
$stream = fopen('php://temp', 'r+'); |
|
$loop = $this->createLoopMock(); |
|
|
|
$capturedData = null; |
|
|
|
$conn = new Stream($stream, $loop); |
|
$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($conn->bufferSize, strlen($capturedData)); |
|
} |
|
|
|
/** |
|
* @covers React\Stream\Stream::__construct |
|
* @covers React\Stream\Stream::handleData |
|
*/ |
|
public function testDataEventDoesEmitOneChunkUntilStreamEndsWhenBufferSizeIsInfinite() |
|
{ |
|
$stream = fopen('php://temp', 'r+'); |
|
$loop = $this->createLoopMock(); |
|
|
|
$capturedData = null; |
|
|
|
$conn = new Stream($stream, $loop); |
|
$conn->bufferSize = null; |
|
|
|
$conn->on('data', function ($data) use (&$capturedData) { |
|
$capturedData = $data; |
|
}); |
|
|
|
fwrite($stream, str_repeat("a", 100000)); |
|
rewind($stream); |
|
|
|
$conn->handleData($stream); |
|
|
|
$this->assertFalse($conn->isReadable()); |
|
$this->assertEquals(100000, strlen($capturedData)); |
|
} |
|
|
|
/** |
|
* @covers React\Stream\Stream::handleData |
|
*/ |
|
public function testEmptyStreamShouldNotEmitData() |
|
{ |
|
$stream = fopen('php://temp', 'r+'); |
|
$loop = $this->createLoopMock(); |
|
|
|
$conn = new Stream($stream, $loop); |
|
$conn->on('data', $this->expectCallableNever()); |
|
|
|
$conn->handleData($stream); |
|
} |
|
|
|
/** |
|
* @covers React\Stream\Stream::write |
|
*/ |
|
public function testWrite() |
|
{ |
|
$stream = fopen('php://temp', 'r+'); |
|
$loop = $this->createWriteableLoopMock(); |
|
|
|
$conn = new Stream($stream, $loop); |
|
$conn->write("foo\n"); |
|
|
|
rewind($stream); |
|
$this->assertSame("foo\n", fgets($stream)); |
|
} |
|
|
|
/** |
|
* @covers React\Stream\Stream::end |
|
*/ |
|
public function testEnd() |
|
{ |
|
$stream = fopen('php://temp', 'r+'); |
|
$loop = $this->createLoopMock(); |
|
|
|
$conn = new Stream($stream, $loop); |
|
$conn->end(); |
|
|
|
$this->assertFalse(is_resource($stream)); |
|
} |
|
|
|
public function testBufferEventsShouldBubbleUp() |
|
{ |
|
$stream = fopen('php://temp', 'r+'); |
|
$loop = $this->createLoopMock(); |
|
|
|
$conn = new Stream($stream, $loop); |
|
|
|
$conn->on('drain', $this->expectCallableOnce()); |
|
$conn->on('error', $this->expectCallableOnce()); |
|
|
|
$buffer = $conn->getBuffer(); |
|
$buffer->emit('drain'); |
|
$buffer->emit('error', array(new \RuntimeException('Whoops'))); |
|
} |
|
|
|
/** |
|
* @covers React\Stream\Stream::handleData |
|
*/ |
|
public function testClosingStreamInDataEventShouldNotTriggerError() |
|
{ |
|
$stream = fopen('php://temp', 'r+'); |
|
$loop = $this->createLoopMock(); |
|
|
|
$conn = new Stream($stream, $loop); |
|
$conn->on('data', function ($data, $stream) { |
|
$stream->close(); |
|
}); |
|
|
|
fwrite($stream, "foobar\n"); |
|
rewind($stream); |
|
|
|
$conn->handleData($stream); |
|
} |
|
|
|
/** |
|
* @covers React\Stream\Stream::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 Stream($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\Stream::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 Stream($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->getMock('React\EventLoop\LoopInterface'); |
|
} |
|
}
|
|
|