ILIAS  release_8 Revision v8.19
All Data Structures Namespaces Files Functions Variables Modules Pages
StreamTest.php
Go to the documentation of this file.
1 <?php
2 
4 
6 use Mockery;
8 
9 /******************************************************************************
10  *
11  * This file is part of ILIAS, a powerful learning management system.
12  *
13  * ILIAS is licensed with the GPL-3.0, you should have received a copy
14  * of said license along with the source code.
15  *
16  * If this is not the case or you just want to try ILIAS, you'll find
17  * us at:
18  * https://www.ilias.de
19  * https://github.com/ILIAS-eLearning
20  *
21  *****************************************************************************/
32 class StreamTest extends TestCase
33 {
37  public static $functions;
38 
39  private function createResource($content, $mode)
40  {
41  //call the root fopen function \ required!
42  return \fopen("data://text/plain,$content", $mode);
43  }
44 
48  protected function setUp(): void
49  {
50  parent::setUp();
51 
52  self::$functions = Mockery::mock();
53  }
54 
59  public function testDetachWhichShouldSucceed(): void
60  {
61  $content = 'awesome content stream';
62  $mode = 'r';
63  $resource = $this->createResource($content, $mode);
64 
65  $subject = new Stream($resource);
66  $detachedResource = $subject->detach();
67 
68  //check that the resource is valid.
69  $this->assertTrue(is_resource($detachedResource));
70  $this->assertSame($resource, $detachedResource);
71 
72  //Can't test the subject because psr-7 defines that the stream is in an unusable after the detach operation.
73  }
74 
80  {
81  $content = 'awesome content stream';
82  $mode = 'r';
83  $resource = $this->createResource($content, $mode);
84 
85  $subject = new Stream($resource);
86 
87  //check that the detached resource is valid.
88  $detachedResource = $subject->detach();
89  $this->assertTrue(is_resource($detachedResource));
90 
91  //must be null because the stream was already detached.
92  $detachedResource = $subject->detach();
93  $this->assertNull($detachedResource);
94  }
95 
101  {
102  $content = 'awesome content stream';
103  $correctSize = strlen($content);
104  $mode = 'r';
105  $resource = $this->createResource($content, $mode);
106 
107  $subject = new Stream($resource);
108 
109  $size = $subject->getSize();
110  $this->assertSame($correctSize, $size);
111  }
112 
118  {
119  $content = 'awesome content stream';
120  $correctSize = 900;
121  $mode = 'r';
122  $resource = $this->createResource($content, $mode);
123  $options = new StreamOptions([], $correctSize);
124 
125  $subject = new Stream($resource, $options);
126 
127  $size = $subject->getSize();
128  $this->assertSame($correctSize, $size);
129  }
130 
136  {
137  $content = 'awesome content stream';
138  $mode = 'r';
139  $resource = $this->createResource($content, $mode);
140 
141  $subject = new Stream($resource);
142  $subject->detach();
143 
144  $size = $subject->getSize();
145  $this->assertNull($size);
146  }
147 
152  public function testCloseWhichShouldSucceed(): void
153  {
154  $content = 'awesome content stream';
155  $mode = 'r';
156  $resource = $this->createResource($content, $mode);
157 
158  $subject = new Stream($resource);
159 
160  $subject->close();
161  $this->assertFalse(is_resource($resource));
162  }
163 
169  {
170  $content = 'awesome content stream';
171  $mode = 'r';
172  $resource = $this->createResource($content, $mode);
173 
174  $subject = new Stream($resource);
175 
176  $actualResource = $subject->detach();
177  $subject->close();
178 
179  $this->assertTrue(is_resource($actualResource));
180  }
181 
186  public function testTellWhichShouldSucceed(): void
187  {
188  $content = 'awesome content stream';
189  $mode = 'r';
190  $offset = 5;
191  $resource = $this->createResource($content, $mode);
192  fseek($resource, $offset);
193 
194  $subject = new Stream($resource);
195 
196  $actualPosition = $subject->tell();
197  $this->assertSame($offset, $actualPosition);
198  }
199 
205  {
206  $content = 'awesome content stream';
207  $mode = 'r';
208  $resource = $this->createResource($content, $mode);
209 
210  $subject = new Stream($resource);
211  $subject->detach();
212 
213  $this->expectException(\RuntimeException::class);
214  $this->expectExceptionMessage('Stream is detached');
215 
216  $subject->tell();
217  }
218 
224  {
225  $content = 'awesome content stream';
226  $mode = 'r';
227  $resource = $this->createResource($content, $mode);
228 
229  //load mock class
230  $functionMock = Mockery::mock('alias:' . PHPStreamFunctions::class);
231  $functionMock->shouldReceive('ftell')
232  ->once()
233  ->with($resource)
234  ->andReturn(false);
235 
236  $functionMock->shouldReceive('fclose')
237  ->once()
238  ->with($resource);
239 
240  $subject = new Stream($resource);
241 
242  $this->expectException(\RuntimeException::class);
243  $this->expectExceptionMessage('Unable to determine stream position');
244 
245  $subject->tell();
246  }
247 
252  public function testEofWhichShouldSucceed(): void
253  {
254  $content = 'awesome content stream';
255  $mode = 'r';
256  $offset = strlen($content); // end of stream
257  $resource = $this->createResource($content, $mode);
258  fseek($resource, $offset); // seek to end of stream
259  fgets($resource, 2); // we need to hit the end of the stream or eof returns false. (https://bugs.php.net/bug.php?id=35136)
260 
261  $subject = new Stream($resource);
262 
263  $endOfFileReached = $subject->eof();
264  $this->assertTrue($endOfFileReached);
265  }
266 
272  {
273  $content = 'awesome content stream';
274  $mode = 'r';
275  $resource = $this->createResource($content, $mode);
276 
277  $subject = new Stream($resource);
278  $subject->detach();
279 
280  $this->expectException(\RuntimeException::class);
281  $this->expectExceptionMessage('Stream is detached');
282 
283  $subject->eof();
284  }
285 
286 
291  public function testSeekWhichShouldSucceed(): void
292  {
293  $content = 'awesome content stream';
294  $mode = 'r';
295  $offset = 5;
296  $resource = $this->createResource($content, $mode);
297 
298  $subject = new Stream($resource);
299 
300  $subject->seek($offset);
301  $this->assertSame($offset, ftell($resource));
302  }
303 
309  {
310  $content = 'awesome content stream';
311  $mode = 'r';
312  $offset = 5;
313  $resource = $this->createResource($content, $mode);
314 
315  $subject = new Stream($resource);
316  $subject->detach();
317 
318  $this->expectException(\RuntimeException::class);
319  $this->expectExceptionMessage('Stream is detached');
320 
321  $subject->seek($offset);
322  }
323 
329  {
330  $content = 'awesome content stream';
331  $mode = 'r';
332  $offset = 5;
333  $resource = $this->createResource($content, $mode);
334 
335  $subjectMock = Mockery::mock(Stream::class . '[isSeekable]', [$resource]);
336 
337  $subjectMock
338  ->shouldReceive('isSeekable')
339  ->once()
340  ->andReturn(false);
341 
342  $this->expectException(\RuntimeException::class);
343  $this->expectExceptionMessage('Stream is not seekable');
344 
345  $subjectMock->seek($offset);
346  }
347 
353  {
354  $content = 'awesome content stream';
355  $mode = 'r';
356  $offset = 5;
357  $whence = SEEK_SET;
358  $resource = $this->createResource($content, $mode);
359 
360  $subject = new Stream($resource);
361 
362  //load mock class
363  $functionMock = Mockery::mock('alias:' . PHPStreamFunctions::class);
364  $functionMock->shouldReceive('fseek')
365  ->once()
366  ->withArgs([$resource, $offset, $whence])
367  ->andReturn(-1);
368 
369  $functionMock->shouldReceive('fclose')
370  ->once()
371  ->with($resource);
372 
373  $this->expectException(\RuntimeException::class);
374  $this->expectExceptionMessage("Unable to seek to stream position \"$offset\" with whence \"$whence\"");
375 
376  $subject->seek($offset);
377  }
378 
383  public function testReadWhichShouldSucceed(): void
384  {
385  $content = 'awesome content stream';
386  $expectedResult = "awesome";
387  $mode = 'r';
388  $length = 7;
389  $resource = $this->createResource($content, $mode);
390 
391  $subject = new Stream($resource);
392 
393  $text = $subject->read($length);
394  $this->assertSame($expectedResult, $text);
395  }
396 
402  {
403  $content = 'awesome content stream';
404  $expectedResult = "";
405  $mode = 'r';
406  $length = 0;
407  $resource = $this->createResource($content, $mode);
408 
409  $subject = new Stream($resource);
410 
411  $text = $subject->read($length);
412  $this->assertSame($expectedResult, $text);
413  }
414 
420  {
421  $content = 'awesome content stream';
422  $mode = 'r';
423  $length = 7;
424  $resource = $this->createResource($content, $mode);
425 
426  $subject = new Stream($resource);
427  $subject->detach();
428 
429  $this->expectException(\RuntimeException::class);
430  $this->expectExceptionMessage('Stream is detached');
431 
432  $subject->read($length);
433  }
434 
440  {
441  $content = 'awesome content stream';
442  $mode = 'r';
443  $length = -2;
444  $resource = $this->createResource($content, $mode);
445 
446  $subject = new Stream($resource);
447 
448  $this->expectException(\RuntimeException::class);
449  $this->expectExceptionMessage('Length parameter must not be negative');
450 
451  $subject->read($length);
452  }
453 
459  {
460  $content = 'awesome content stream';
461  $mode = 'w';
462  $length = 3;
463  $resource = $this->createResource($content, $mode);
464 
465  $subject = new Stream($resource);
466 
467  $this->expectException(\RuntimeException::class);
468  $this->expectExceptionMessage('Can not read from non-readable stream');
469 
470  $subject->read($length);
471  }
472 
478  {
479  $content = 'awesome content stream';
480  $mode = 'r';
481  $length = 3;
482  $resource = $this->createResource($content, $mode);
483 
484  $subject = new Stream($resource);
485 
486  //load mock class
487  $functionMock = Mockery::mock('alias:' . PHPStreamFunctions::class);
488 
489  $functionMock->shouldReceive('fread')
490  ->once()
491  ->withArgs([$resource, $length])
492  ->andReturn(false);
493 
494  $functionMock->shouldReceive('fclose')
495  ->once()
496  ->with($resource);
497 
498  $this->expectException(\RuntimeException::class);
499  $this->expectExceptionMessage('Unable to read from stream');
500 
501  $subject->read($length);
502  }
503 
508  public function testGetContentsWhichShouldSucceed(): void
509  {
510  $content = 'awesome content stream';
511  $mode = 'r';
512  $resource = $this->createResource($content, $mode);
513 
514  $subject = new Stream($resource);
515 
516  $text = $subject->getContents();
517  $this->assertSame($content, $text);
518  }
519 
525  {
526  $content = 'awesome content stream';
527  $mode = 'r';
528  $resource = $this->createResource($content, $mode);
529 
530  $subject = new Stream($resource);
531  $subject->detach();
532 
533  $this->expectException(\RuntimeException::class);
534  $this->expectExceptionMessage('Stream is detached');
535 
536  $subject->getContents();
537  }
538 
544  {
545  $content = 'awesome content stream';
546  $mode = 'r';
547  $resource = $this->createResource($content, $mode);
548 
549  $subject = new Stream($resource);
550 
551  //load mock class
552  $functionMock = Mockery::mock('alias:' . PHPStreamFunctions::class);
553 
554  $functionMock->shouldReceive('stream_get_contents')
555  ->once()
556  ->with($resource)
557  ->andReturn(false);
558 
559  $functionMock->shouldReceive('fclose')
560  ->once()
561  ->with($resource);
562 
563  $this->expectException(\RuntimeException::class);
564  $this->expectExceptionMessage('Unable to read stream contents');
565 
566  $subject->getContents();
567  }
568 
573  public function testToStringWhichShouldSucceed(): void
574  {
575  $content = 'awesome content stream';
576  $mode = 'r';
577  $resource = $this->createResource($content, $mode);
578 
579  $subject = new Stream($resource);
580 
581  $text = $subject->__toString();
582  $this->assertSame($content, $text);
583  }
584 
592  {
593  $content = 'awesome content stream';
594  $expectedResult = '';
595  $mode = 'r';
596  $resource = $this->createResource($content, $mode);
597 
598  $subject = Mockery::mock(Stream::class . '[rewind]', [$resource]);
599 
600  $subject->shouldDeferMissing();
601  $subject->shouldReceive('rewind')
602  ->once()
603  ->andThrow(\RuntimeException::class);
604 
605  $text = $subject->__toString();
606  $this->assertSame($expectedResult, $text);
607  }
608 
613  public function testWriteWhichShouldSucceed(): void
614  {
615  $content = 'awesome content stream';
616  $newContent = '!';
617  $byteCount = strlen($newContent);
618  $mode = 'r+';
619  $resource = fopen('php://memory', $mode);
620  PHPStreamFunctions::fwrite($resource, $content);
621 
622  $subject = new Stream($resource);
623  $currentSize = $subject->getSize();
624 
625  $numberOfBytesWritten = $subject->write($newContent);
626  $newSize = $subject->getSize();
627 
628  $this->assertSame($byteCount, $numberOfBytesWritten, 'The count of bytes passed to write must match the written bytes after the operation.');
629  $this->assertGreaterThan($currentSize, $newSize, 'The new size must be grater than the old size because we wrote to the stream.');
630  }
631 
637  {
638  $content = 'awesome content stream';
639  $newContent = '!';
640  $mode = 'w';
641  $resource = $this->createResource($content, $mode);
642 
643  $subject = new Stream($resource);
644  $subject->detach();
645 
646  $this->expectException(\RuntimeException::class);
647  $this->expectExceptionMessage('Stream is detached');
648 
649  $subject->write($newContent);
650  }
651 
657  {
658  $content = 'awesome content stream';
659  $newContent = '!';
660  $mode = 'r';
661  $resource = $this->createResource($content, $mode);
662 
663  $subject = new Stream($resource);
664 
665  $this->expectException(\RuntimeException::class);
666  $this->expectExceptionMessage('Can not write to a non-writable stream');
667 
668  $subject->write($newContent);
669  }
670 
676  {
677  $content = 'awesome content stream';
678  $newContent = '!';
679  $mode = 'a+';
680  $resource = $this->createResource($content, $mode);
681 
682  $subject = new Stream($resource);
683 
684  //load mock class
685  $functionMock = Mockery::mock('alias:' . PHPStreamFunctions::class);
686 
687  $functionMock->shouldReceive('fwrite')
688  ->once()
689  ->withArgs([$resource, $newContent])
690  ->andReturn(false);
691 
692  $functionMock->shouldReceive('fclose')
693  ->once()
694  ->with($resource);
695 
696  $this->expectException(\RuntimeException::class);
697  $this->expectExceptionMessage('Unable to write to stream');
698 
699  $subject->write($newContent);
700  }
701 }
static fwrite($handle, string $string, ?int $length=null)