AppendStreamTest.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. <?php
  2. declare(strict_types=1);
  3. namespace GuzzleHttp\Tests\Psr7;
  4. use GuzzleHttp\Psr7;
  5. use GuzzleHttp\Psr7\AppendStream;
  6. use PHPUnit\Framework\TestCase;
  7. use Psr\Http\Message\StreamInterface;
  8. class AppendStreamTest extends TestCase
  9. {
  10. public function testValidatesStreamsAreReadable(): void
  11. {
  12. $a = new AppendStream();
  13. $s = $this->createMock(StreamInterface::class);
  14. $s->expects(self::once())
  15. ->method('isReadable')
  16. ->willReturn(false);
  17. $this->expectException(\InvalidArgumentException::class);
  18. $this->expectExceptionMessage('Each stream must be readable');
  19. $a->addStream($s);
  20. }
  21. public function testValidatesSeekType(): void
  22. {
  23. $a = new AppendStream();
  24. $this->expectException(\RuntimeException::class);
  25. $this->expectExceptionMessage('The AppendStream can only seek with SEEK_SET');
  26. $a->seek(100, SEEK_CUR);
  27. }
  28. public function testTriesToRewindOnSeek(): void
  29. {
  30. $a = new AppendStream();
  31. $s = $this->createMock(StreamInterface::class);
  32. $s->expects(self::once())
  33. ->method('isReadable')
  34. ->willReturn(true);
  35. $s->expects(self::once())
  36. ->method('isSeekable')
  37. ->willReturn(true);
  38. $s->expects(self::once())
  39. ->method('rewind')
  40. ->willThrowException(new \RuntimeException());
  41. $a->addStream($s);
  42. $this->expectException(\RuntimeException::class);
  43. $this->expectExceptionMessage('Unable to seek stream 0 of the AppendStream');
  44. $a->seek(10);
  45. }
  46. public function testSeeksToPositionByReading(): void
  47. {
  48. $a = new AppendStream([
  49. Psr7\Utils::streamFor('foo'),
  50. Psr7\Utils::streamFor('bar'),
  51. Psr7\Utils::streamFor('baz'),
  52. ]);
  53. $a->seek(3);
  54. self::assertSame(3, $a->tell());
  55. self::assertSame('bar', $a->read(3));
  56. $a->seek(6);
  57. self::assertSame(6, $a->tell());
  58. self::assertSame('baz', $a->read(3));
  59. }
  60. public function testDetachWithoutStreams(): void
  61. {
  62. $s = new AppendStream();
  63. $s->detach();
  64. self::assertSame(0, $s->getSize());
  65. self::assertTrue($s->eof());
  66. self::assertTrue($s->isReadable());
  67. self::assertSame('', (string) $s);
  68. self::assertTrue($s->isSeekable());
  69. self::assertFalse($s->isWritable());
  70. }
  71. public function testDetachesEachStream(): void
  72. {
  73. $handle = fopen('php://temp', 'r');
  74. $s1 = Psr7\Utils::streamFor($handle);
  75. $s2 = Psr7\Utils::streamFor('bar');
  76. $a = new AppendStream([$s1, $s2]);
  77. $a->detach();
  78. self::assertSame(0, $a->getSize());
  79. self::assertTrue($a->eof());
  80. self::assertTrue($a->isReadable());
  81. self::assertSame('', (string) $a);
  82. self::assertTrue($a->isSeekable());
  83. self::assertFalse($a->isWritable());
  84. self::assertNull($s1->detach());
  85. self::assertIsResource($handle, 'resource is not closed when detaching');
  86. fclose($handle);
  87. }
  88. public function testClosesEachStream(): void
  89. {
  90. $handle = fopen('php://temp', 'r');
  91. $s1 = Psr7\Utils::streamFor($handle);
  92. $s2 = Psr7\Utils::streamFor('bar');
  93. $a = new AppendStream([$s1, $s2]);
  94. $a->close();
  95. self::assertSame(0, $a->getSize());
  96. self::assertTrue($a->eof());
  97. self::assertTrue($a->isReadable());
  98. self::assertSame('', (string) $a);
  99. self::assertTrue($a->isSeekable());
  100. self::assertFalse($a->isWritable());
  101. self::assertFalse(is_resource($handle));
  102. }
  103. public function testIsNotWritable(): void
  104. {
  105. $a = new AppendStream([Psr7\Utils::streamFor('foo')]);
  106. self::assertFalse($a->isWritable());
  107. self::assertTrue($a->isSeekable());
  108. self::assertTrue($a->isReadable());
  109. $this->expectException(\RuntimeException::class);
  110. $this->expectExceptionMessage('Cannot write to an AppendStream');
  111. $a->write('foo');
  112. }
  113. public function testDoesNotNeedStreams(): void
  114. {
  115. $a = new AppendStream();
  116. self::assertSame('', (string) $a);
  117. }
  118. public function testCanReadFromMultipleStreams(): void
  119. {
  120. $a = new AppendStream([
  121. Psr7\Utils::streamFor('foo'),
  122. Psr7\Utils::streamFor('bar'),
  123. Psr7\Utils::streamFor('baz'),
  124. ]);
  125. self::assertFalse($a->eof());
  126. self::assertSame(0, $a->tell());
  127. self::assertSame('foo', $a->read(3));
  128. self::assertSame('bar', $a->read(3));
  129. self::assertSame('baz', $a->read(3));
  130. self::assertSame('', $a->read(1));
  131. self::assertTrue($a->eof());
  132. self::assertSame(9, $a->tell());
  133. self::assertSame('foobarbaz', (string) $a);
  134. }
  135. public function testCanDetermineSizeFromMultipleStreams(): void
  136. {
  137. $a = new AppendStream([
  138. Psr7\Utils::streamFor('foo'),
  139. Psr7\Utils::streamFor('bar'),
  140. ]);
  141. self::assertSame(6, $a->getSize());
  142. $s = $this->createMock(StreamInterface::class);
  143. $s->expects(self::once())
  144. ->method('isSeekable')
  145. ->willReturn(false);
  146. $s->expects(self::once())
  147. ->method('isReadable')
  148. ->willReturn(true);
  149. $a->addStream($s);
  150. self::assertNull($a->getSize());
  151. }
  152. /**
  153. * @requires PHP < 7.4
  154. */
  155. public function testCatchesExceptionsWhenCastingToString(): void
  156. {
  157. $s = $this->createMock(StreamInterface::class);
  158. $s->expects(self::once())
  159. ->method('isSeekable')
  160. ->willReturn(true);
  161. $s->expects(self::once())
  162. ->method('read')
  163. ->willThrowException(new \RuntimeException('foo'));
  164. $s->expects(self::once())
  165. ->method('isReadable')
  166. ->willReturn(true);
  167. $s->expects(self::any())
  168. ->method('eof')
  169. ->willReturn(false);
  170. $a = new AppendStream([$s]);
  171. self::assertFalse($a->eof());
  172. $errors = [];
  173. set_error_handler(static function (int $errorNumber, string $errorMessage) use (&$errors): bool {
  174. $errors[] = ['number' => $errorNumber, 'message' => $errorMessage];
  175. return true;
  176. });
  177. (string) $a;
  178. restore_error_handler();
  179. self::assertCount(1, $errors);
  180. self::assertSame(E_USER_ERROR, $errors[0]['number']);
  181. self::assertStringStartsWith('GuzzleHttp\Psr7\AppendStream::__toString exception:', $errors[0]['message']);
  182. }
  183. public function testReturnsEmptyMetadata(): void
  184. {
  185. $s = new AppendStream();
  186. self::assertSame([], $s->getMetadata());
  187. self::assertNull($s->getMetadata('foo'));
  188. }
  189. }