CachingStreamTest.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. <?php
  2. declare(strict_types=1);
  3. namespace GuzzleHttp\Tests\Psr7;
  4. use GuzzleHttp\Psr7;
  5. use GuzzleHttp\Psr7\CachingStream;
  6. use GuzzleHttp\Psr7\Stream;
  7. use PHPUnit\Framework\TestCase;
  8. /**
  9. * @covers \GuzzleHttp\Psr7\CachingStream
  10. */
  11. class CachingStreamTest extends TestCase
  12. {
  13. /** @var CachingStream */
  14. private $body;
  15. /** @var Stream */
  16. private $decorated;
  17. protected function setUp(): void
  18. {
  19. $this->decorated = Psr7\Utils::streamFor('testing');
  20. $this->body = new CachingStream($this->decorated);
  21. }
  22. protected function tearDown(): void
  23. {
  24. $this->decorated->close();
  25. $this->body->close();
  26. }
  27. public function testUsesRemoteSizeIfAvailable(): void
  28. {
  29. $body = Psr7\Utils::streamFor('test');
  30. $caching = new CachingStream($body);
  31. self::assertSame(4, $caching->getSize());
  32. }
  33. public function testUsesRemoteSizeIfNotAvailable(): void
  34. {
  35. $body = new Psr7\PumpStream(function () {
  36. return 'a';
  37. });
  38. $caching = new CachingStream($body);
  39. self::assertNull($caching->getSize());
  40. }
  41. public function testReadsUntilCachedToByte(): void
  42. {
  43. $this->body->seek(5);
  44. self::assertSame('n', $this->body->read(1));
  45. $this->body->seek(0);
  46. self::assertSame('t', $this->body->read(1));
  47. }
  48. public function testCanSeekNearEndWithSeekEnd(): void
  49. {
  50. $baseStream = Psr7\Utils::streamFor(implode('', range('a', 'z')));
  51. $cached = new CachingStream($baseStream);
  52. $cached->seek(-1, SEEK_END);
  53. self::assertSame(25, $baseStream->tell());
  54. self::assertSame('z', $cached->read(1));
  55. self::assertSame(26, $cached->getSize());
  56. }
  57. public function testCanSeekToEndWithSeekEnd(): void
  58. {
  59. $baseStream = Psr7\Utils::streamFor(implode('', range('a', 'z')));
  60. $cached = new CachingStream($baseStream);
  61. $cached->seek(0, SEEK_END);
  62. self::assertSame(26, $baseStream->tell());
  63. self::assertSame('', $cached->read(1));
  64. self::assertSame(26, $cached->getSize());
  65. }
  66. public function testCanUseSeekEndWithUnknownSize(): void
  67. {
  68. $baseStream = Psr7\Utils::streamFor('testing');
  69. $decorated = Psr7\FnStream::decorate($baseStream, [
  70. 'getSize' => function () {
  71. return null;
  72. },
  73. ]);
  74. $cached = new CachingStream($decorated);
  75. $cached->seek(-1, SEEK_END);
  76. self::assertSame('g', $cached->read(1));
  77. }
  78. public function testRewind(): void
  79. {
  80. $a = Psr7\Utils::streamFor('foo');
  81. $d = new CachingStream($a);
  82. self::assertSame('foo', $d->read(3));
  83. $d->rewind();
  84. self::assertSame('foo', $d->read(3));
  85. }
  86. public function testCanSeekToReadBytes(): void
  87. {
  88. self::assertSame('te', $this->body->read(2));
  89. $this->body->seek(0);
  90. self::assertSame('test', $this->body->read(4));
  91. self::assertSame(4, $this->body->tell());
  92. $this->body->seek(2);
  93. self::assertSame(2, $this->body->tell());
  94. $this->body->seek(2, SEEK_CUR);
  95. self::assertSame(4, $this->body->tell());
  96. self::assertSame('ing', $this->body->read(3));
  97. }
  98. public function testCanSeekToReadBytesWithPartialBodyReturned(): void
  99. {
  100. $stream = fopen('php://temp', 'r+');
  101. fwrite($stream, 'testing');
  102. fseek($stream, 0);
  103. $this->decorated = $this->getMockBuilder(Stream::class)
  104. ->setConstructorArgs([$stream])
  105. ->onlyMethods(['read'])
  106. ->getMock();
  107. $this->decorated->expects(self::exactly(2))
  108. ->method('read')
  109. ->willReturnCallback(function (int $length) use ($stream) {
  110. return fread($stream, 2);
  111. });
  112. $this->body = new CachingStream($this->decorated);
  113. self::assertSame(0, $this->body->tell());
  114. $this->body->seek(4, SEEK_SET);
  115. self::assertSame(4, $this->body->tell());
  116. $this->body->seek(0);
  117. self::assertSame('test', $this->body->read(4));
  118. }
  119. public function testWritesToBufferStream(): void
  120. {
  121. $this->body->read(2);
  122. $this->body->write('hi');
  123. $this->body->seek(0);
  124. self::assertSame('tehiing', (string) $this->body);
  125. }
  126. public function testSkipsOverwrittenBytes(): void
  127. {
  128. $decorated = Psr7\Utils::streamFor(
  129. implode("\n", array_map(function ($n) {
  130. return str_pad((string) $n, 4, '0', STR_PAD_LEFT);
  131. }, range(0, 25)))
  132. );
  133. $body = new CachingStream($decorated);
  134. self::assertSame("0000\n", Psr7\Utils::readLine($body));
  135. self::assertSame("0001\n", Psr7\Utils::readLine($body));
  136. // Write over part of the body yet to be read, so skip some bytes
  137. self::assertSame(5, $body->write("TEST\n"));
  138. // Read, which skips bytes, then reads
  139. self::assertSame("0003\n", Psr7\Utils::readLine($body));
  140. self::assertSame("0004\n", Psr7\Utils::readLine($body));
  141. self::assertSame("0005\n", Psr7\Utils::readLine($body));
  142. // Overwrite part of the cached body (so don't skip any bytes)
  143. $body->seek(5);
  144. self::assertSame(5, $body->write("ABCD\n"));
  145. self::assertSame("TEST\n", Psr7\Utils::readLine($body));
  146. self::assertSame("0003\n", Psr7\Utils::readLine($body));
  147. self::assertSame("0004\n", Psr7\Utils::readLine($body));
  148. self::assertSame("0005\n", Psr7\Utils::readLine($body));
  149. self::assertSame("0006\n", Psr7\Utils::readLine($body));
  150. self::assertSame(5, $body->write("1234\n"));
  151. // Seek to 0 and ensure the overwritten bit is replaced
  152. $body->seek(0);
  153. self::assertSame("0000\nABCD\nTEST\n0003\n0004\n0005\n0006\n1234\n0008\n0009\n", $body->read(50));
  154. // Ensure that casting it to a string does not include the bit that was overwritten
  155. self::assertStringContainsString("0000\nABCD\nTEST\n0003\n0004\n0005\n0006\n1234\n0008\n0009\n", (string) $body);
  156. }
  157. public function testClosesBothStreams(): void
  158. {
  159. $s = fopen('php://temp', 'r');
  160. $a = Psr7\Utils::streamFor($s);
  161. $d = new CachingStream($a);
  162. $d->close();
  163. self::assertFalse(is_resource($s));
  164. }
  165. public function testEnsuresValidWhence(): void
  166. {
  167. $this->expectException(\InvalidArgumentException::class);
  168. $this->expectExceptionMessage('Invalid whence');
  169. $this->body->seek(10, -123456);
  170. }
  171. }