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.

285 lines
7.4 KiB

<?php
/**
* Slim Framework (https://slimframework.com)
*
* @license https://github.com/slimphp/Slim-Psr7/blob/master/LICENSE.md (MIT License)
*/
declare(strict_types=1);
namespace Slim\Tests\Psr7;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use ReflectionException;
use ReflectionMethod;
use ReflectionProperty;
use RuntimeException;
use Slim\Psr7\Stream;
use function fopen;
use function popen;
use function stream_get_contents;
use function trim;
class StreamTest extends TestCase
{
use ProphecyTrait;
/**
* @var resource pipe stream file handle
*/
private $pipeFh;
private Stream $pipeStream;
public function tearDown(): void
{
if ($this->pipeFh != null) {
// prevent broken pipe error message
stream_get_contents($this->pipeFh);
}
}
public function testIsPipe()
{
$this->openPipeStream();
$this->assertTrue($this->pipeStream->isPipe());
$this->pipeStream->detach();
$this->assertFalse($this->pipeStream->isPipe());
$fhFile = fopen(__FILE__, 'r');
$fileStream = new Stream($fhFile);
$this->assertFalse($fileStream->isPipe());
}
public function testIsPipeReadable()
{
$this->openPipeStream();
$this->assertTrue($this->pipeStream->isReadable());
}
public function testPipeIsNotSeekable()
{
$this->openPipeStream();
$this->assertFalse($this->pipeStream->isSeekable());
}
public function testCannotSeekPipe()
{
$this->expectException(RuntimeException::class);
$this->openPipeStream();
$this->pipeStream->seek(0);
}
public function testCannotTellPipe()
{
$this->expectException(RuntimeException::class);
$this->openPipeStream();
$this->pipeStream->tell();
}
public function testCannotRewindPipe()
{
$this->expectException(RuntimeException::class);
$this->openPipeStream();
$this->pipeStream->rewind();
}
public function testPipeGetSizeYieldsNull()
{
$this->openPipeStream();
$this->assertNull($this->pipeStream->getSize());
}
public function testClosePipe()
{
$this->openPipeStream();
// prevent broken pipe error message
stream_get_contents($this->pipeFh);
$this->pipeStream->close();
$this->pipeFh = null;
$this->assertFalse($this->pipeStream->isPipe());
}
public function testPipeToString()
{
$this->openPipeStream();
$content = trim((string) $this->pipeStream);
$this->assertSame('12', $content);
}
public function testConvertsToStringPartiallyReadNonSeekableStream()
{
$this->openPipeStream();
$head = $this->pipeStream->read(1);
$tail = trim((string) $this->pipeStream);
$this->assertSame('1', $head);
$this->assertSame('2', $tail);
}
public function testPipeGetContents()
{
$this->openPipeStream();
$contents = trim($this->pipeStream->getContents());
$this->assertSame('12', $contents);
}
public function testIsWriteable()
{
$resource = fopen('php://temp', 'w');
$stream = new Stream($resource);
$this->assertEquals(13, $stream->write('Hello, world!'));
$this->assertTrue($stream->isWritable());
}
public function testIsReadable()
{
$resource = fopen('php://temp', 'r');
$stream = new Stream($resource);
$this->assertTrue($stream->isReadable());
$this->assertFalse($stream->isWritable());
}
public function testIsWritableAndReadable()
{
$resource = fopen('php://temp', 'w+');
$stream = new Stream($resource);
$stream->write('Hello, world!');
$this->assertEquals('Hello, world!', $stream);
$this->assertTrue($stream->isWritable());
$this->assertTrue($stream->isReadable());
}
/**
* Test that a call to the protected method `attach` would invoke `detach`.
*
* @throws ReflectionException
*/
public function testAttachAgain()
{
$this->openPipeStream();
$streamProphecy = $this->prophesize(Stream::class);
/** @noinspection PhpUndefinedMethodInspection */
$streamProphecy->detach()->shouldBeCalled();
/** @var Stream $stream */
$stream = $streamProphecy->reveal();
$streamProperty = new ReflectionProperty(Stream::class, 'stream');
$streamProperty->setAccessible(true);
$streamProperty->setValue($stream, $this->pipeFh);
$attachMethod = new ReflectionMethod(Stream::class, 'attach');
$attachMethod->setAccessible(true);
$attachMethod->invoke($stream, $this->pipeFh);
}
public function testGetMetaDataReturnsNullIfStreamIsDetached()
{
$resource = fopen('php://temp', 'rw+');
$stream = new Stream($resource);
$stream->detach();
$this->assertNull($stream->getMetadata());
}
private function openPipeStream()
{
$this->pipeFh = popen('echo 12', 'r');
$this->pipeStream = new Stream($this->pipeFh);
}
public function testReadOnlyCachedStreamsAreDisallowed()
{
$resource = fopen('php://temp', 'w+');
$cache = new Stream(fopen('php://temp', 'r'));
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('Cache stream must be seekable and writable');
new Stream($resource, $cache);
}
public function testNonSeekableCachedStreamsAreDisallowed()
{
$resource = fopen('php://temp', 'w+');
$cache = new Stream(fopen('php://output', 'w'));
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('Cache stream must be seekable and writable');
new Stream($resource, $cache);
}
public function testCachedStreamsGetsContentFromTheCache()
{
$resource = popen('echo HelloWorld', 'r');
$stream = new Stream($resource, new Stream(fopen('php://temp', 'w+')));
$this->assertEquals("HelloWorld\n", $stream->getContents());
$this->assertEquals("HelloWorld\n", $stream->getContents());
}
public function testCachedStreamsFillsCacheOnRead()
{
$resource = fopen('data://,0', 'r');
$stream = new Stream($resource, new Stream(fopen('php://temp', 'w+')));
$this->assertEquals("0", $stream->read(100));
$this->assertEquals("0", $stream->__toString());
}
public function testDetachingStreamDropsCache()
{
$cache = new Stream(fopen('php://temp', 'w+'));
$resource = fopen('data://,foo', 'r');
$stream = new Stream($resource, $cache);
$stream->detach();
$cacheProperty = new ReflectionProperty(Stream::class, 'cache');
$cacheProperty->setAccessible(true);
$finishedProperty = new ReflectionProperty(Stream::class, 'finished');
$finishedProperty->setAccessible(true);
$this->assertNull($cacheProperty->getValue($stream));
$this->assertFalse($finishedProperty->getValue($stream));
}
public function testCachedStreamsRewindIfFinishedOnToString()
{
$resource = fopen('data://,foo', 'r');
$stream = new Stream($resource, new Stream(fopen('php://temp', 'w+')));
$this->assertEquals('foo', (string)$stream);
$this->assertEquals('foo', (string)$stream);
}
}