1<?php
2namespace GuzzleHttp\Tests\Stream;
3
4use GuzzleHttp\Stream\AsyncReadStream;
5use GuzzleHttp\Stream\BufferStream;
6use GuzzleHttp\Stream\FnStream;
7use GuzzleHttp\Stream\Stream;
8
9class AsyncReadStreamTest extends \PHPUnit_Framework_TestCase
10{
11    /**
12     * @expectedException \InvalidArgumentException
13     * @expectedExceptionMessage Buffer must be readable and writable
14     */
15    public function testValidatesReadableBuffer()
16    {
17        new AsyncReadStream(FnStream::decorate(
18            Stream::factory(),
19            ['isReadable' => function () { return false; }]
20        ));
21    }
22
23    /**
24     * @expectedException \InvalidArgumentException
25     * @expectedExceptionMessage Buffer must be readable and writable
26     */
27    public function testValidatesWritableBuffer()
28    {
29        new AsyncReadStream(FnStream::decorate(
30            Stream::factory(),
31            ['isWritable' => function () { return false; }]
32        ));
33    }
34
35    public function testValidatesHwmMetadata()
36    {
37        $a = new AsyncReadStream(Stream::factory(), [
38            'drain' => function() {}
39        ]);
40        $this->assertNull($this->readAttribute($a, 'drain'));
41    }
42
43    /**
44     * @expectedException \InvalidArgumentException
45     * @expectedExceptionMessage pump must be callable
46     */
47    public function testValidatesPumpIsCallable()
48    {
49        new AsyncReadStream(new BufferStream(), ['pump' => true]);
50    }
51
52    /**
53     * @expectedException \InvalidArgumentException
54     * @expectedExceptionMessage drain must be callable
55     */
56    public function testValidatesDrainIsCallable()
57    {
58        new AsyncReadStream(new BufferStream(), ['drain' => true]);
59    }
60
61    public function testCanInitialize()
62    {
63        $buffer = new BufferStream();
64        $a = new AsyncReadStream($buffer, [
65            'size'  => 10,
66            'drain' => function () {},
67            'pump'  => function () {},
68        ]);
69        $this->assertSame($buffer, $this->readAttribute($a, 'stream'));
70        $this->assertTrue(is_callable($this->readAttribute($a, 'drain')));
71        $this->assertTrue(is_callable($this->readAttribute($a, 'pump')));
72        $this->assertTrue($a->isReadable());
73        $this->assertFalse($a->isSeekable());
74        $this->assertFalse($a->isWritable());
75        $this->assertFalse($a->write('foo'));
76        $this->assertEquals(10, $a->getSize());
77    }
78
79    public function testReadsFromBufferWithNoDrainOrPump()
80    {
81        $buffer = new BufferStream();
82        $a = new AsyncReadStream($buffer);
83        $buffer->write('foo');
84        $this->assertNull($a->getSize());
85        $this->assertEquals('foo', $a->read(10));
86        $this->assertEquals('', $a->read(10));
87    }
88
89    public function testCallsPumpForMoreDataWhenRequested()
90    {
91        $called = 0;
92        $buffer = new BufferStream();
93        $a = new AsyncReadStream($buffer, [
94            'pump' => function ($size) use (&$called) {
95                $called++;
96                return str_repeat('.', $size);
97            }
98        ]);
99        $buffer->write('foobar');
100        $this->assertEquals('foo', $a->read(3));
101        $this->assertEquals(0, $called);
102        $this->assertEquals('bar.....', $a->read(8));
103        $this->assertEquals(1, $called);
104        $this->assertEquals('..', $a->read(2));
105        $this->assertEquals(2, $called);
106    }
107
108    public function testCallsDrainWhenNeeded()
109    {
110        $called = 0;
111        $buffer = new BufferStream(5);
112        $a = new AsyncReadStream($buffer, [
113            'drain' => function (BufferStream $b) use (&$called, $buffer) {
114                $this->assertSame($b, $buffer);
115                $called++;
116            }
117        ]);
118
119        $buffer->write('foobar');
120        $this->assertEquals(6, $buffer->getSize());
121        $this->assertEquals(0, $called);
122
123        $a->read(3);
124        $this->assertTrue($this->readAttribute($a, 'needsDrain'));
125        $this->assertEquals(3, $buffer->getSize());
126        $this->assertEquals(0, $called);
127
128        $a->read(3);
129        $this->assertEquals(0, $buffer->getSize());
130        $this->assertFalse($this->readAttribute($a, 'needsDrain'));
131        $this->assertEquals(1, $called);
132    }
133
134    public function testCreatesBufferWithNoConfig()
135    {
136        list($buffer, $async) = AsyncReadStream::create();
137        $this->assertInstanceOf('GuzzleHttp\Stream\BufferStream', $buffer);
138        $this->assertInstanceOf('GuzzleHttp\Stream\AsyncReadStream', $async);
139    }
140
141    public function testCreatesBufferWithSpecifiedBuffer()
142    {
143        $buf = new BufferStream();
144        list($buffer, $async) = AsyncReadStream::create(['buffer' => $buf]);
145        $this->assertSame($buf, $buffer);
146        $this->assertInstanceOf('GuzzleHttp\Stream\AsyncReadStream', $async);
147    }
148
149    public function testCreatesNullStream()
150    {
151        list($buffer, $async) = AsyncReadStream::create(['max_buffer' => 0]);
152        $this->assertInstanceOf('GuzzleHttp\Stream\NullStream', $buffer);
153        $this->assertInstanceOf('GuzzleHttp\Stream\AsyncReadStream', $async);
154    }
155
156    public function testCreatesDroppingStream()
157    {
158        list($buffer, $async) = AsyncReadStream::create(['max_buffer' => 5]);
159        $this->assertInstanceOf('GuzzleHttp\Stream\DroppingStream', $buffer);
160        $this->assertInstanceOf('GuzzleHttp\Stream\AsyncReadStream', $async);
161        $buffer->write('12345678910');
162        $this->assertEquals(5, $buffer->getSize());
163    }
164
165    public function testCreatesOnWriteStream()
166    {
167        $c = 0;
168        $b = new BufferStream();
169        list($buffer, $async) = AsyncReadStream::create([
170            'buffer' => $b,
171            'write'  => function (BufferStream $buf, $data) use (&$c, $b) {
172                $this->assertSame($buf, $b);
173                $this->assertEquals('foo', $data);
174                $c++;
175            },
176        ]);
177        $this->assertInstanceOf('GuzzleHttp\Stream\FnStream', $buffer);
178        $this->assertInstanceOf('GuzzleHttp\Stream\AsyncReadStream', $async);
179        $this->assertEquals(0, $c);
180        $this->assertEquals(3, $buffer->write('foo'));
181        $this->assertEquals(1, $c);
182        $this->assertEquals(3, $buffer->write('foo'));
183        $this->assertEquals(2, $c);
184        $this->assertEquals('foofoo', (string) $buffer);
185    }
186}
187