1<?php
2namespace GuzzleHttp\Tests\Stream;
3
4use GuzzleHttp\Stream\AppendStream;
5use GuzzleHttp\Stream\Exception\CannotAttachException;
6use GuzzleHttp\Stream\Stream;
7use PHPUnit\Framework\TestCase;
8
9class AppendStreamTest extends TestCase
10{
11    /**
12     * @expectedException \InvalidArgumentException
13     * @expectedExceptionMessage Each stream must be readable
14     */
15    public function testValidatesStreamsAreReadable()
16    {
17        $a = new AppendStream();
18        $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface')
19            ->getMockForAbstractClass();
20        $s->expects($this->once())
21            ->method('isReadable')
22            ->will($this->returnValue(true));
23        $a->addStream($s);
24    }
25
26    public function testValidatesSeekType()
27    {
28        $a = new AppendStream();
29        $this->assertFalse($a->seek(100, SEEK_CUR));
30    }
31
32    public function testTriesToRewindOnSeek()
33    {
34        $a = new AppendStream();
35        $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface')
36            ->getMockForAbstractClass();
37        $s->expects($this->once())
38            ->method('isReadable')
39            ->will($this->returnValue(true));
40        $s->expects($this->once())
41            ->method('isSeekable')
42            ->will($this->returnValue(true));
43        $s->expects($this->once())
44            ->method('seek')
45            ->will($this->returnValue(false));
46        $a->addStream($s);
47        $this->assertFalse($a->seek(10));
48    }
49
50    public function testSeeksToPositionByReading()
51    {
52        $a = new AppendStream([
53            Stream::factory('foo'),
54            Stream::factory('bar'),
55            Stream::factory('baz'),
56        ]);
57
58        $this->assertTrue($a->seek(3));
59        $this->assertEquals(3, $a->tell());
60        $this->assertEquals('bar', $a->read(3));
61        $a->seek(6);
62        $this->assertEquals(6, $a->tell());
63        $this->assertEquals('baz', $a->read(3));
64    }
65
66    public function testDetachesEachStream()
67    {
68        $s1 = Stream::factory('foo');
69        $s2 = Stream::factory('foo');
70        $a = new AppendStream([$s1, $s2]);
71        $this->assertSame('foofoo', (string) $a);
72        $a->detach();
73        $this->assertSame('', (string) $a);
74        $this->assertSame(0, $a->getSize());
75    }
76
77    public function testClosesEachStream()
78    {
79        $s1 = Stream::factory('foo');
80        $a = new AppendStream([$s1]);
81        $a->close();
82        $this->assertSame('', (string) $a);
83    }
84
85    public function testIsNotWritable()
86    {
87        $a = new AppendStream([Stream::factory('foo')]);
88        $this->assertFalse($a->isWritable());
89        $this->assertTrue($a->isSeekable());
90        $this->assertTrue($a->isReadable());
91        $this->assertFalse($a->write('foo'));
92    }
93
94    public function testDoesNotNeedStreams()
95    {
96        $a = new AppendStream();
97        $this->assertEquals('', (string) $a);
98    }
99
100    public function testCanReadFromMultipleStreams()
101    {
102        $a = new AppendStream([
103            Stream::factory('foo'),
104            Stream::factory('bar'),
105            Stream::factory('baz'),
106        ]);
107        $this->assertFalse($a->eof());
108        $this->assertSame(0, $a->tell());
109        $this->assertEquals('foo', $a->read(3));
110        $this->assertEquals('bar', $a->read(3));
111        $this->assertEquals('baz', $a->read(3));
112        $this->assertEmpty($a->read(1));
113        $this->assertTrue($a->eof());
114        $this->assertSame(9, $a->tell());
115        $this->assertEquals('foobarbaz', (string) $a);
116    }
117
118    public function testCanDetermineSizeFromMultipleStreams()
119    {
120        $a = new AppendStream([
121            Stream::factory('foo'),
122            Stream::factory('bar'),
123        ]);
124        $this->assertEquals(6, $a->getSize());
125
126        $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface')
127            ->getMockForAbstractClass();
128        $s->expects($this->once())
129            ->method('isSeekable')
130            ->will($this->returnValue(null));
131        $s->expects($this->once())
132            ->method('isReadable')
133            ->will($this->returnValue(true));
134        $a->addStream($s);
135        $this->assertNull($a->getSize());
136    }
137
138    public function testCatchesExceptionsWhenCastingToString()
139    {
140        $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface')
141            ->getMockForAbstractClass();
142        $s->expects($this->once())
143            ->method('read')
144            ->will($this->throwException(new \RuntimeException('foo')));
145        $s->expects($this->once())
146            ->method('isReadable')
147            ->will($this->returnValue(true));
148        $s->expects($this->any())
149            ->method('eof')
150            ->will($this->returnValue(false));
151        $a = new AppendStream([$s]);
152        $this->assertFalse($a->eof());
153        $this->assertSame('', (string) $a);
154    }
155
156    /**
157     * @doesNotPerformAssertions
158     */
159    public function testCanDetach()
160    {
161        $s = new AppendStream();
162        $s->detach();
163    }
164
165    public function testReturnsEmptyMetadata()
166    {
167        $s = new AppendStream();
168        $this->assertEquals([], $s->getMetadata());
169        $this->assertNull($s->getMetadata('foo'));
170    }
171
172    public function testCannotAttach()
173    {
174        $p = new AppendStream();
175        $this->expectException(CannotAttachException::class);
176        $p->attach('a');
177    }
178}
179