Skip to content

Commit c155800

Browse files
authored
Merge pull request #75 from clue-labs/strict
Obey strict method and event semantics for closed and piped streams
2 parents aed3c66 + 8b0e828 commit c155800

6 files changed

Lines changed: 142 additions & 4 deletions

File tree

src/Stream.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public function resume()
9999
public function write($data)
100100
{
101101
if (!$this->writable) {
102-
return;
102+
return false;
103103
}
104104

105105
return $this->buffer->write($data);

src/ThroughStream.php

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
class ThroughStream extends CompositeStream
66
{
7+
private $paused = false;
8+
79
public function __construct()
810
{
911
$readable = new ReadableStream();
@@ -17,19 +19,35 @@ public function filter($data)
1719
return $data;
1820
}
1921

22+
public function pause()
23+
{
24+
parent::pause();
25+
$this->paused = true;
26+
}
27+
28+
public function resume()
29+
{
30+
parent::resume();
31+
$this->paused = false;
32+
}
33+
2034
public function write($data)
2135
{
22-
if (!$this->writable) {
36+
if (!$this->writable->isWritable()) {
2337
return false;
2438
}
2539

2640
$this->readable->emit('data', array($this->filter($data)));
2741

28-
return true;
42+
return $this->writable->isWritable() && !$this->paused;
2943
}
3044

3145
public function end($data = null)
3246
{
47+
if (!$this->writable->isWritable()) {
48+
return;
49+
}
50+
3351
if (null !== $data) {
3452
$this->readable->emit('data', array($this->filter($data)));
3553
}

tests/BufferedSinkTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,15 @@ public function writeShouldTriggerProgressOnPromise()
133133
$sink->end('baz');
134134
}
135135

136+
/** @test */
137+
public function writeAfterEndShouldReturnFalse()
138+
{
139+
$sink = new BufferedSink();
140+
$sink->end();
141+
142+
$this->assertFalse($sink->write('foo'));
143+
}
144+
136145
/** @test */
137146
public function forwardedErrorsFromPipeShouldRejectPromise()
138147
{

tests/CompositeStreamTest.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,44 @@ public function itShouldHandlePipingCorrectly()
125125
$input->emit('data', array('foo'));
126126
}
127127

128+
/** @test */
129+
public function itShouldForwardPauseUpstreamWhenPipedTo()
130+
{
131+
$readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
132+
$readable->expects($this->any())->method('isReadable')->willReturn(true);
133+
$writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
134+
$writable->expects($this->any())->method('isWritable')->willReturn(true);
135+
136+
$composite = new CompositeStream($readable, $writable);
137+
138+
$input = $this->getMockBuilder('React\Stream\ReadableStream')->setMethods(array('pause', 'resume'))->getMock();
139+
$input
140+
->expects($this->once())
141+
->method('pause');
142+
143+
$input->pipe($composite);
144+
$composite->pause();
145+
}
146+
147+
/** @test */
148+
public function itShouldForwardResumeUpstreamWhenPipedTo()
149+
{
150+
$readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
151+
$readable->expects($this->any())->method('isReadable')->willReturn(true);
152+
$writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
153+
$writable->expects($this->any())->method('isWritable')->willReturn(true);
154+
155+
$composite = new CompositeStream($readable, $writable);
156+
157+
$input = $this->getMockBuilder('React\Stream\ReadableStream')->setMethods(array('pause', 'resume'))->getMock();
158+
$input
159+
->expects($this->once())
160+
->method('resume');
161+
162+
$input->pipe($composite);
163+
$composite->resume();
164+
}
165+
128166
/** @test */
129167
public function itShouldForwardPauseAndResumeUpstreamWhenPipedTo()
130168
{

tests/StreamTest.php

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,32 @@ public function testCloseShouldEmitCloseEvent()
5757
$this->assertFalse($conn->isReadable());
5858
}
5959

60+
public function testEndShouldEndBuffer()
61+
{
62+
$stream = fopen('php://temp', 'r+');
63+
$loop = $this->createLoopMock();
64+
65+
$buffer = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
66+
$buffer->expects($this->once())->method('end')->with('foo');
67+
68+
$conn = new Stream($stream, $loop, $buffer);
69+
$conn->end('foo');
70+
}
71+
72+
73+
public function testEndAfterCloseIsNoOp()
74+
{
75+
$stream = fopen('php://temp', 'r+');
76+
$loop = $this->createLoopMock();
77+
78+
$buffer = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
79+
$buffer->expects($this->never())->method('end');
80+
81+
$conn = new Stream($stream, $loop);
82+
$conn->close();
83+
$conn->end();
84+
}
85+
6086
/**
6187
* @covers React\Stream\Stream::__construct
6288
* @covers React\Stream\Stream::handleData
@@ -193,7 +219,7 @@ public function testEndedStreamsShouldNotWrite()
193219
$stream = fopen($file, 'r');
194220

195221
$this->assertSame("foo\n", fgets($stream));
196-
$this->assertNull($res);
222+
$this->assertFalse($res);
197223

198224
unlink($file);
199225
}

tests/ThroughStreamTest.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,53 @@ public function endShouldWriteDataBeforeClosing()
6262
$this->assertFalse($through->isWritable());
6363
}
6464

65+
/** @test */
66+
public function endTwiceShouldOnlyEmitOnce()
67+
{
68+
$through = new ThroughStream();
69+
$through->on('data', $this->expectCallableOnce('first'));
70+
$through->end('first');
71+
$through->end('ignored');
72+
}
73+
74+
/** @test */
75+
public function writeAfterEndShouldReturnFalse()
76+
{
77+
$through = new ThroughStream();
78+
$through->on('data', $this->expectCallableNever());
79+
$through->end();
80+
81+
$this->assertFalse($through->write('foo'));
82+
}
83+
84+
/** @test */
85+
public function writeDataWillCloseStreamShouldReturnFalse()
86+
{
87+
$through = new ThroughStream();
88+
$through->on('data', array($through, 'close'));
89+
90+
$this->assertFalse($through->write('foo'));
91+
}
92+
93+
/** @test */
94+
public function writeDataToPausedShouldReturnFalse()
95+
{
96+
$through = new ThroughStream();
97+
$through->pause();
98+
99+
$this->assertFalse($through->write('foo'));
100+
}
101+
102+
/** @test */
103+
public function writeDataToResumedShouldReturnTrue()
104+
{
105+
$through = new ThroughStream();
106+
$through->pause();
107+
$through->resume();
108+
109+
$this->assertTrue($through->write('foo'));
110+
}
111+
65112
/** @test */
66113
public function itShouldBeReadableByDefault()
67114
{

0 commit comments

Comments
 (0)