1<?php
2namespace GuzzleHttp\Tests\Stream;
3
4use GuzzleHttp\Stream\Stream;
5use InvalidArgumentException;
6use PHPUnit\Framework\TestCase;
7
8/**
9 * @covers GuzzleHttp\Stream\Stream
10 */
11class StreamTest extends TestCase
12{
13    public function testConstructorThrowsExceptionOnInvalidArgument()
14    {
15        $this->expectException(InvalidArgumentException::class);
16        new Stream(true);
17    }
18
19    public function testConstructorInitializesProperties()
20    {
21        $handle = fopen('php://temp', 'r+');
22        fwrite($handle, 'data');
23        $stream = new Stream($handle);
24        $this->assertTrue($stream->isReadable());
25        $this->assertTrue($stream->isWritable());
26        $this->assertTrue($stream->isSeekable());
27        $this->assertEquals('php://temp', $stream->getMetadata('uri'));
28        $this->assertIsArray($stream->getMetadata());
29        $this->assertEquals(4, $stream->getSize());
30        $this->assertFalse($stream->eof());
31        $stream->close();
32    }
33
34    public function testStreamClosesHandleOnDestruct()
35    {
36        $handle = fopen('php://temp', 'r');
37        $stream = new Stream($handle);
38        unset($stream);
39        $this->assertFalse(is_resource($handle));
40    }
41
42    public function testConvertsToString()
43    {
44        $handle = fopen('php://temp', 'w+');
45        fwrite($handle, 'data');
46        $stream = new Stream($handle);
47        $this->assertEquals('data', (string) $stream);
48        $this->assertEquals('data', (string) $stream);
49        $stream->close();
50    }
51
52    public function testGetsContents()
53    {
54        $handle = fopen('php://temp', 'w+');
55        fwrite($handle, 'data');
56        $stream = new Stream($handle);
57        $this->assertEquals('', $stream->getContents());
58        $stream->seek(0);
59        $this->assertEquals('data', $stream->getContents());
60        $this->assertEquals('', $stream->getContents());
61    }
62
63    public function testChecksEof()
64    {
65        $handle = fopen('php://temp', 'w+');
66        fwrite($handle, 'data');
67        $stream = new Stream($handle);
68        $this->assertFalse($stream->eof());
69        $stream->read(4);
70        $this->assertTrue($stream->eof());
71        $stream->close();
72    }
73
74    public function testAllowsSettingManualSize()
75    {
76        $handle = fopen('php://temp', 'w+');
77        fwrite($handle, 'data');
78        $stream = new Stream($handle);
79        $stream->setSize(10);
80        $this->assertEquals(10, $stream->getSize());
81        $stream->close();
82    }
83
84    public function testGetSize()
85    {
86        $size = filesize(__FILE__);
87        $handle = fopen(__FILE__, 'r');
88        $stream = new Stream($handle);
89        $this->assertEquals($size, $stream->getSize());
90        // Load from cache
91        $this->assertEquals($size, $stream->getSize());
92        $stream->close();
93    }
94
95    public function testEnsuresSizeIsConsistent()
96    {
97        $h = fopen('php://temp', 'w+');
98        $this->assertEquals(3, fwrite($h, 'foo'));
99        $stream = new Stream($h);
100        $this->assertEquals(3, $stream->getSize());
101        $this->assertEquals(4, $stream->write('test'));
102        $this->assertEquals(7, $stream->getSize());
103        $this->assertEquals(7, $stream->getSize());
104        $stream->close();
105    }
106
107    public function testProvidesStreamPosition()
108    {
109        $handle = fopen('php://temp', 'w+');
110        $stream = new Stream($handle);
111        $this->assertEquals(0, $stream->tell());
112        $stream->write('foo');
113        $this->assertEquals(3, $stream->tell());
114        $stream->seek(1);
115        $this->assertEquals(1, $stream->tell());
116        $this->assertSame(ftell($handle), $stream->tell());
117        $stream->close();
118    }
119
120    public function testKeepsPositionOfResource()
121    {
122        $h = fopen(__FILE__, 'r');
123        fseek($h, 10);
124        $stream = Stream::factory($h);
125        $this->assertEquals(10, $stream->tell());
126        $stream->close();
127    }
128
129    public function testCanDetachAndAttachStream()
130    {
131        $r = fopen('php://temp', 'w+');
132        $stream = new Stream($r);
133        $stream->write('foo');
134        $this->assertTrue($stream->isReadable());
135        $this->assertSame($r, $stream->detach());
136        $this->assertNull($stream->detach());
137
138        $this->assertFalse($stream->isReadable());
139        $this->assertFalse($stream->read(10));
140        $this->assertFalse($stream->isWritable());
141        $this->assertFalse($stream->write('bar'));
142        $this->assertFalse($stream->isSeekable());
143        $this->assertFalse($stream->seek(10));
144        $this->assertFalse($stream->tell());
145        $this->assertTrue($stream->eof());
146        $this->assertNull($stream->getSize());
147        $this->assertSame('', (string) $stream);
148        $this->assertSame('', $stream->getContents());
149
150        $stream->attach($r);
151        $stream->seek(0);
152        $this->assertEquals('foo', $stream->getContents());
153        $this->assertTrue($stream->isReadable());
154        $this->assertTrue($stream->isWritable());
155        $this->assertTrue($stream->isSeekable());
156
157        $stream->close();
158    }
159
160    public function testCloseClearProperties()
161    {
162        $handle = fopen('php://temp', 'r+');
163        $stream = new Stream($handle);
164        $stream->close();
165
166        $this->assertEmpty($stream->getMetadata());
167        $this->assertFalse($stream->isSeekable());
168        $this->assertFalse($stream->isReadable());
169        $this->assertFalse($stream->isWritable());
170        $this->assertNull($stream->getSize());
171    }
172
173    public function testCreatesWithFactory()
174    {
175        $stream = Stream::factory('foo');
176        $this->assertInstanceOf('GuzzleHttp\Stream\Stream', $stream);
177        $this->assertEquals('foo', $stream->getContents());
178        $stream->close();
179    }
180
181    public function testFactoryCreatesFromEmptyString()
182    {
183        $s = Stream::factory();
184        $this->assertInstanceOf('GuzzleHttp\Stream\Stream', $s);
185    }
186
187    public function testFactoryCreatesFromResource()
188    {
189        $r = fopen(__FILE__, 'r');
190        $s = Stream::factory($r);
191        $this->assertInstanceOf('GuzzleHttp\Stream\Stream', $s);
192        $this->assertSame(file_get_contents(__FILE__), (string) $s);
193    }
194
195    public function testFactoryCreatesFromObjectWithToString()
196    {
197        $r = new HasToString();
198        $s = Stream::factory($r);
199        $this->assertInstanceOf('GuzzleHttp\Stream\Stream', $s);
200        $this->assertEquals('foo', (string) $s);
201    }
202
203    public function testCreatePassesThrough()
204    {
205        $s = Stream::factory('foo');
206        $this->assertSame($s, Stream::factory($s));
207    }
208
209    public function testThrowsExceptionForUnknown()
210    {
211        $this->expectException(InvalidArgumentException::class);
212        Stream::factory(new \stdClass());
213    }
214
215    public function testReturnsCustomMetadata()
216    {
217        $s = Stream::factory('foo', ['metadata' => ['hwm' => 3]]);
218        $this->assertEquals(3, $s->getMetadata('hwm'));
219        $this->assertArrayHasKey('hwm', $s->getMetadata());
220    }
221
222    public function testCanSetSize()
223    {
224        $s = Stream::factory('', ['size' => 10]);
225        $this->assertEquals(10, $s->getSize());
226    }
227
228    public function testCanCreateIteratorBasedStream()
229    {
230        $a = new \ArrayIterator(['foo', 'bar', '123']);
231        $p = Stream::factory($a);
232        $this->assertInstanceOf('GuzzleHttp\Stream\PumpStream', $p);
233        $this->assertEquals('foo', $p->read(3));
234        $this->assertFalse($p->eof());
235        $this->assertEquals('b', $p->read(1));
236        $this->assertEquals('a', $p->read(1));
237        $this->assertEquals('r12', $p->read(3));
238        $this->assertFalse($p->eof());
239        $this->assertEquals('3', $p->getContents());
240        $this->assertTrue($p->eof());
241        $this->assertEquals(9, $p->tell());
242    }
243}
244
245class HasToString
246{
247    public function __toString() {
248        return 'foo';
249    }
250}
251