1<?php
2namespace GuzzleHttp\Tests\Http;
3
4use GuzzleHttp\Stream\Exception\SeekException;
5use GuzzleHttp\Stream\FnStream;
6use GuzzleHttp\Stream\Stream;
7use GuzzleHttp\Stream\LimitStream;
8use GuzzleHttp\Stream\NoSeekStream;
9use PHPUnit\Framework\TestCase;
10
11/**
12 * @covers GuzzleHttp\Stream\LimitStream
13 */
14class LimitStreamTest extends TestCase
15{
16    /** @var LimitStream */
17    protected $body;
18
19    /** @var Stream */
20    protected $decorated;
21
22    public function setUp(): void
23    {
24        $this->decorated = Stream::factory(fopen(__FILE__, 'r'));
25        $this->body = new LimitStream($this->decorated, 10, 3);
26    }
27
28    public function testReturnsSubset()
29    {
30        $body = new LimitStream(Stream::factory('foo'), -1, 1);
31        $this->assertEquals('oo', (string) $body);
32        $this->assertTrue($body->eof());
33        $body->seek(0);
34        $this->assertFalse($body->eof());
35        $this->assertEquals('oo', $body->read(100));
36        $this->assertEmpty($body->read(1));
37        $this->assertTrue($body->eof());
38    }
39
40    public function testReturnsSubsetWhenCastToString()
41    {
42        $body = Stream::factory('foo_baz_bar');
43        $limited = new LimitStream($body, 3, 4);
44        $this->assertEquals('baz', (string) $limited);
45    }
46
47    public function testReturnsSubsetOfEmptyBodyWhenCastToString()
48    {
49        $body = Stream::factory('');
50        $limited = new LimitStream($body, 0, 10);
51        $this->assertEquals('', (string) $limited);
52    }
53
54    public function testSeeksWhenConstructed()
55    {
56        $this->assertEquals(0, $this->body->tell());
57        $this->assertEquals(3, $this->decorated->tell());
58    }
59
60    public function testAllowsBoundedSeek()
61    {
62        $this->assertEquals(true, $this->body->seek(100));
63        $this->assertEquals(10, $this->body->tell());
64        $this->assertEquals(13, $this->decorated->tell());
65        $this->assertEquals(true, $this->body->seek(0));
66        $this->assertEquals(0, $this->body->tell());
67        $this->assertEquals(3, $this->decorated->tell());
68        $this->assertEquals(false, $this->body->seek(-10));
69        $this->assertEquals(0, $this->body->tell());
70        $this->assertEquals(3, $this->decorated->tell());
71        $this->assertEquals(true, $this->body->seek(5));
72        $this->assertEquals(5, $this->body->tell());
73        $this->assertEquals(8, $this->decorated->tell());
74        $this->assertEquals(false, $this->body->seek(1000, SEEK_END));
75    }
76
77    public function testReadsOnlySubsetOfData()
78    {
79        $data = $this->body->read(100);
80        $this->assertEquals(10, strlen($data));
81        $this->assertFalse($this->body->read(1000));
82
83        $this->body->setOffset(10);
84        $newData = $this->body->read(100);
85        $this->assertEquals(10, strlen($newData));
86        $this->assertNotSame($data, $newData);
87    }
88
89    public function testThrowsWhenCurrentGreaterThanOffsetSeek()
90    {
91        $this->expectException(SeekException::class);
92        $this->expectExceptionMessage('Could not seek the stream to position 2');
93        $a = Stream::factory('foo_bar');
94        $b = new NoSeekStream($a);
95        $c = new LimitStream($b);
96        $a->getContents();
97        $c->setOffset(2);
98    }
99
100    public function testClaimsConsumedWhenReadLimitIsReached()
101    {
102        $this->assertFalse($this->body->eof());
103        $this->body->read(1000);
104        $this->assertTrue($this->body->eof());
105    }
106
107    public function testContentLengthIsBounded()
108    {
109        $this->assertEquals(10, $this->body->getSize());
110    }
111
112    public function testGetContentsIsBasedOnSubset()
113    {
114        $body = new LimitStream(Stream::factory('foobazbar'), 3, 3);
115        $this->assertEquals('baz', $body->getContents());
116    }
117
118    public function testReturnsNullIfSizeCannotBeDetermined()
119    {
120        $a = new FnStream([
121            'getSize' => function () { return null; },
122            'tell'    => function () { return 0; },
123        ]);
124        $b = new LimitStream($a);
125        $this->assertNull($b->getSize());
126    }
127
128    public function testLengthLessOffsetWhenNoLimitSize()
129    {
130        $a = Stream::factory('foo_bar');
131        $b = new LimitStream($a, -1, 4);
132        $this->assertEquals(3, $b->getSize());
133    }
134}
135