1<?php
2namespace GuzzleHttp\Tests\Stream;
3
4use GuzzleHttp\Stream\AppendStream;
5use GuzzleHttp\Stream\Stream;
6
7class AppendStreamTest extends \PHPUnit_Framework_TestCase
8{
9    /**
10     * @expectedException \InvalidArgumentException
11     * @expectedExceptionMessage Each stream must be readable
12     */
13    public function testValidatesStreamsAreReadable()
14    {
15        $a = new AppendStream();
16        $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface')
17            ->setMethods(['isReadable'])
18            ->getMockForAbstractClass();
19        $s->expects($this->once())
20            ->method('isReadable')
21            ->will($this->returnValue(false));
22        $a->addStream($s);
23    }
24
25    public function testValidatesSeekType()
26    {
27        $a = new AppendStream();
28        $this->assertFalse($a->seek(100, SEEK_CUR));
29    }
30
31    public function testTriesToRewindOnSeek()
32    {
33        $a = new AppendStream();
34        $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface')
35            ->setMethods(['isReadable', 'seek', 'isSeekable'])
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            ->setMethods(['isSeekable', 'isReadable'])
128            ->getMockForAbstractClass();
129        $s->expects($this->once())
130            ->method('isSeekable')
131            ->will($this->returnValue(null));
132        $s->expects($this->once())
133            ->method('isReadable')
134            ->will($this->returnValue(true));
135        $a->addStream($s);
136        $this->assertNull($a->getSize());
137    }
138
139    public function testCatchesExceptionsWhenCastingToString()
140    {
141        $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface')
142            ->setMethods(['read', 'isReadable', 'eof'])
143            ->getMockForAbstractClass();
144        $s->expects($this->once())
145            ->method('read')
146            ->will($this->throwException(new \RuntimeException('foo')));
147        $s->expects($this->once())
148            ->method('isReadable')
149            ->will($this->returnValue(true));
150        $s->expects($this->any())
151            ->method('eof')
152            ->will($this->returnValue(false));
153        $a = new AppendStream([$s]);
154        $this->assertFalse($a->eof());
155        $this->assertSame('', (string) $a);
156    }
157
158    public function testCanDetach()
159    {
160        $s = new AppendStream();
161        $s->detach();
162    }
163
164    public function testReturnsEmptyMetadata()
165    {
166        $s = new AppendStream();
167        $this->assertEquals([], $s->getMetadata());
168        $this->assertNull($s->getMetadata('foo'));
169    }
170
171    /**
172     * @expectedException \GuzzleHttp\Stream\Exception\CannotAttachException
173     */
174    public function testCannotAttach()
175    {
176        $p = new AppendStream();
177        $p->attach('a');
178    }
179}
180