c5ecdd80460bafa69772bf6d5ab1085d7af2d8ed
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Http / StreamTest.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Unit\Http;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Http\Stream;
18
19 /**
20 * Testcase for \TYPO3\CMS\Core\Http\StreamTest
21 *
22 * Adapted from https://github.com/phly/http/
23 */
24 class StreamTest extends \TYPO3\Components\TestingFramework\Core\UnitTestCase
25 {
26 /**
27 * @var Stream
28 */
29 protected $stream;
30
31 protected function setUp()
32 {
33 $this->stream = new Stream('php://memory', 'wb+');
34 }
35
36 /**
37 * @test
38 */
39 public function canInstantiateWithStreamIdentifier()
40 {
41 $this->assertInstanceOf(Stream::class, $this->stream);
42 }
43
44 /**
45 * @test
46 */
47 public function canInstantiteWithStreamResource()
48 {
49 $resource = fopen('php://memory', 'wb+');
50 $stream = new Stream($resource);
51 $this->assertInstanceOf(Stream::class, $stream);
52 }
53
54 /**
55 * @test
56 */
57 public function isReadableReturnsFalseIfStreamIsNotReadable()
58 {
59 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
60 touch($fileName);
61 $this->testFilesToDelete[] = $fileName;
62 $stream = new Stream($fileName, 'w');
63 $this->assertFalse($stream->isReadable());
64 }
65
66 /**
67 * @test
68 */
69 public function isWritableReturnsFalseIfStreamIsNotWritable()
70 {
71 $stream = new Stream('php://memory', 'r');
72 $this->assertFalse($stream->isWritable());
73 }
74
75 /**
76 * @test
77 */
78 public function toStringRetrievesFullContentsOfStream()
79 {
80 $message = 'foo bar';
81 $this->stream->write($message);
82 $this->assertEquals($message, (string) $this->stream);
83 }
84
85 /**
86 * @test
87 */
88 public function detachReturnsResource()
89 {
90 $resource = fopen('php://memory', 'wb+');
91 $stream = new Stream($resource);
92 $this->assertSame($resource, $stream->detach());
93 }
94
95 /**
96 * @test
97 */
98 public function constructorRaisesExceptionWhenPassingInvalidStreamResource()
99 {
100 $this->expectException(\InvalidArgumentException::class);
101 new Stream([' THIS WILL NOT WORK ']);
102 }
103
104 /**
105 * @test
106 */
107 public function toStringSerializationReturnsEmptyStringWhenStreamIsNotReadable()
108 {
109 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
110 touch($fileName);
111 $this->testFilesToDelete[] = $fileName;
112 file_put_contents($fileName, 'FOO BAR');
113 $stream = new Stream($fileName, 'w');
114
115 $this->assertEquals('', $stream->__toString());
116 }
117
118 /**
119 * @test
120 */
121 public function closeClosesResource()
122 {
123 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
124 touch($fileName);
125 $this->testFilesToDelete[] = $fileName;
126 $resource = fopen($fileName, 'wb+');
127 $stream = new Stream($resource);
128 $stream->close();
129 $this->assertFalse(is_resource($resource));
130 }
131
132 /**
133 * @test
134 */
135 public function closeUnsetsResource()
136 {
137 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
138 touch($fileName);
139 $this->testFilesToDelete[] = $fileName;
140 $resource = fopen($fileName, 'wb+');
141 $stream = new Stream($resource);
142 $stream->close();
143
144 $this->assertNull($stream->detach());
145 }
146
147 /**
148 * @test
149 */
150 public function closeDoesNothingAfterDetach()
151 {
152 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
153 touch($fileName);
154 $this->testFilesToDelete[] = $fileName;
155 $resource = fopen($fileName, 'wb+');
156 $stream = new Stream($resource);
157 $detached = $stream->detach();
158
159 $stream->close();
160 $this->assertTrue(is_resource($detached));
161 $this->assertSame($resource, $detached);
162 }
163
164 /**
165 * @test
166 */
167 public function getSizeReportsNullWhenNoResourcePresent()
168 {
169 $this->stream->detach();
170 $this->assertNull($this->stream->getSize());
171 }
172
173 /**
174 * @test
175 */
176 public function tellReportsCurrentPositionInResource()
177 {
178 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
179 $this->testFilesToDelete[] = $fileName;
180 file_put_contents($fileName, 'FOO BAR');
181 $resource = fopen($fileName, 'wb+');
182 $stream = new Stream($resource);
183
184 fseek($resource, 2);
185
186 $this->assertEquals(2, $stream->tell());
187 }
188
189 /**
190 * @test
191 */
192 public function tellRaisesExceptionIfResourceIsDetached()
193 {
194 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
195 $this->testFilesToDelete[] = $fileName;
196 file_put_contents($fileName, 'FOO BAR');
197 $resource = fopen($fileName, 'wb+');
198 $stream = new Stream($resource);
199
200 fseek($resource, 2);
201 $stream->detach();
202 $this->expectException(\RuntimeException::class);
203 $this->expectExceptionCode(1436717285);
204 $stream->tell();
205 }
206
207 /**
208 * @test
209 */
210 public function eofReportsFalseWhenNotAtEndOfStream()
211 {
212 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
213 $this->testFilesToDelete[] = $fileName;
214 file_put_contents($fileName, 'FOO BAR');
215 $resource = fopen($fileName, 'wb+');
216 $stream = new Stream($resource);
217
218 fseek($resource, 2);
219 $this->assertFalse($stream->eof());
220 }
221
222 /**
223 * @test
224 */
225 public function eofReportsTrueWhenAtEndOfStream()
226 {
227 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
228 $this->testFilesToDelete[] = $fileName;
229 file_put_contents($fileName, 'FOO BAR');
230 $resource = fopen($fileName, 'wb+');
231 $stream = new Stream($resource);
232
233 while (!feof($resource)) {
234 fread($resource, 4096);
235 }
236 $this->assertTrue($stream->eof());
237 }
238
239 /**
240 * @test
241 */
242 public function eofReportsTrueWhenStreamIsDetached()
243 {
244 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
245 $this->testFilesToDelete[] = $fileName;
246 file_put_contents($fileName, 'FOO BAR');
247 $resource = fopen($fileName, 'wb+');
248 $stream = new Stream($resource);
249
250 fseek($resource, 2);
251 $stream->detach();
252 $this->assertTrue($stream->eof());
253 }
254
255 /**
256 * @test
257 */
258 public function isSeekableReturnsTrueForReadableStreams()
259 {
260 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
261 $this->testFilesToDelete[] = $fileName;
262 file_put_contents($fileName, 'FOO BAR');
263 $resource = fopen($fileName, 'wb+');
264 $stream = new Stream($resource);
265 $this->assertTrue($stream->isSeekable());
266 }
267
268 /**
269 * @test
270 */
271 public function isSeekableReturnsFalseForDetachedStreams()
272 {
273 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
274 $this->testFilesToDelete[] = $fileName;
275 file_put_contents($fileName, 'FOO BAR');
276 $resource = fopen($fileName, 'wb+');
277 $stream = new Stream($resource);
278 $stream->detach();
279 $this->assertFalse($stream->isSeekable());
280 }
281
282 /**
283 * @test
284 */
285 public function seekAdvancesToGivenOffsetOfStream()
286 {
287 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
288 $this->testFilesToDelete[] = $fileName;
289 file_put_contents($fileName, 'FOO BAR');
290 $resource = fopen($fileName, 'wb+');
291 $stream = new Stream($resource);
292 $stream->seek(2);
293 $this->assertEquals(2, $stream->tell());
294 }
295
296 /**
297 * @test
298 */
299 public function rewindResetsToStartOfStream()
300 {
301 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
302 $this->testFilesToDelete[] = $fileName;
303 file_put_contents($fileName, 'FOO BAR');
304 $resource = fopen($fileName, 'wb+');
305 $stream = new Stream($resource);
306 $stream->seek(2);
307 $stream->rewind();
308 $this->assertEquals(0, $stream->tell());
309 }
310
311 /**
312 * @test
313 */
314 public function seekRaisesExceptionWhenStreamIsDetached()
315 {
316 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
317 $this->testFilesToDelete[] = $fileName;
318 file_put_contents($fileName, 'FOO BAR');
319 $resource = fopen($fileName, 'wb+');
320 $stream = new Stream($resource);
321 $stream->detach();
322 $this->expectException(\RuntimeException::class);
323 $this->expectExceptionCode(1436717287);
324 $stream->seek(2);
325 }
326
327 /**
328 * @test
329 */
330 public function isWritableReturnsFalseWhenStreamIsDetached()
331 {
332 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
333 $this->testFilesToDelete[] = $fileName;
334 file_put_contents($fileName, 'FOO BAR');
335 $resource = fopen($fileName, 'wb+');
336 $stream = new Stream($resource);
337 $stream->detach();
338 $this->assertFalse($stream->isWritable());
339 }
340
341 /**
342 * @test
343 */
344 public function writeRaisesExceptionWhenStreamIsDetached()
345 {
346 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
347 $this->testFilesToDelete[] = $fileName;
348 file_put_contents($fileName, 'FOO BAR');
349 $resource = fopen($fileName, 'wb+');
350 $stream = new Stream($resource);
351 $stream->detach();
352 $this->expectException(\RuntimeException::class);
353 $this->expectExceptionCode(1436717290);
354 $stream->write('bar');
355 }
356
357 /**
358 * @test
359 */
360 public function isReadableReturnsFalseWhenStreamIsDetached()
361 {
362 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
363 $this->testFilesToDelete[] = $fileName;
364 file_put_contents($fileName, 'FOO BAR');
365 $resource = fopen($fileName, 'wb+');
366 $stream = new Stream($resource);
367 $stream->detach();
368 $this->assertFalse($stream->isReadable());
369 }
370
371 /**
372 * @test
373 */
374 public function readRaisesExceptionWhenStreamIsDetached()
375 {
376 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
377 $this->testFilesToDelete[] = $fileName;
378 file_put_contents($fileName, 'FOO BAR');
379 $resource = fopen($fileName, 'r');
380 $stream = new Stream($resource);
381 $stream->detach();
382 $this->expectException(\RuntimeException::class);
383 $this->expectExceptionCode(1436717292);
384 $stream->read(4096);
385 }
386
387 /**
388 * @test
389 */
390 public function readReturnsEmptyStringWhenAtEndOfFile()
391 {
392 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
393 $this->testFilesToDelete[] = $fileName;
394 file_put_contents($fileName, 'FOO BAR');
395 $resource = fopen($fileName, 'r');
396 $stream = new Stream($resource);
397 while (!feof($resource)) {
398 fread($resource, 4096);
399 }
400 $this->assertEquals('', $stream->read(4096));
401 }
402
403 /**
404 * @test
405 */
406 public function getContentsReturnsEmptyStringIfStreamIsNotReadable()
407 {
408 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
409 $this->testFilesToDelete[] = $fileName;
410 file_put_contents($fileName, 'FOO BAR');
411 $resource = fopen($fileName, 'w');
412 $stream = new Stream($resource);
413 $this->assertEquals('', $stream->getContents());
414 }
415
416 /**
417 * @return array
418 */
419 public function invalidResourcesDataProvider()
420 {
421 $fileName = tempnam(sys_get_temp_dir(), 'PHLY');
422 $this->testFilesToDelete[] = $fileName;
423
424 return [
425 'null' => [null],
426 'false' => [false],
427 'true' => [true],
428 'int' => [1],
429 'float' => [1.1],
430 'array' => [[fopen($fileName, 'r+')]],
431 'object' => [(object) ['resource' => fopen($fileName, 'r+')]],
432 ];
433 }
434
435 /**
436 * @dataProvider invalidResourcesDataProvider
437 * @test
438 */
439 public function attachWithNonStringNonResourceRaisesExceptionByType($resource)
440 {
441 $this->expectException(\InvalidArgumentException::class);
442 $this->expectExceptionCode(1436717297);
443 $this->stream->attach($resource);
444 }
445
446 /**
447 * @test
448 */
449 public function attachWithNonStringNonResourceRaisesExceptionByString()
450 {
451 $this->expectException(\InvalidArgumentException::class);
452 $this->expectExceptionCode(1436717296);
453 $this->stream->attach('foo-bar-baz');
454 }
455
456 /**
457 * @test
458 */
459 public function attachWithResourceAttachesResource()
460 {
461 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
462 touch($fileName);
463 $this->testFilesToDelete[] = $fileName;
464 $resource = fopen($fileName, 'r+');
465 $this->stream->attach($resource);
466
467 $r = new \ReflectionProperty($this->stream, 'resource');
468 $r->setAccessible(true);
469 $test = $r->getValue($this->stream);
470 $this->assertSame($resource, $test);
471 }
472
473 /**
474 * @test
475 */
476 public function attachWithStringRepresentingResourceCreatesAndAttachesResource()
477 {
478 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
479 touch($fileName);
480 $this->testFilesToDelete[] = $fileName;
481 $this->stream->attach($fileName);
482
483 $resource = fopen($fileName, 'r+');
484 fwrite($resource, 'FooBar');
485
486 $this->stream->rewind();
487 $test = (string) $this->stream;
488 $this->assertEquals('FooBar', $test);
489 }
490
491 /**
492 * @test
493 */
494 public function getContentsShouldGetFullStreamContents()
495 {
496 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
497 touch($fileName);
498 $this->testFilesToDelete[] = $fileName;
499 $resource = fopen($fileName, 'r+');
500 $this->stream->attach($resource);
501
502 fwrite($resource, 'FooBar');
503
504 // rewind, because current pointer is at end of stream!
505 $this->stream->rewind();
506 $test = $this->stream->getContents();
507 $this->assertEquals('FooBar', $test);
508 }
509
510 /**
511 * @test
512 */
513 public function getContentsShouldReturnStreamContentsFromCurrentPointer()
514 {
515 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
516 touch($fileName);
517 $this->testFilesToDelete[] = $fileName;
518 $resource = fopen($fileName, 'r+');
519 $this->stream->attach($resource);
520
521 fwrite($resource, 'FooBar');
522
523 // seek to position 3
524 $this->stream->seek(3);
525 $test = $this->stream->getContents();
526 $this->assertEquals('Bar', $test);
527 }
528
529 /**
530 * @test
531 */
532 public function getMetadataReturnsAllMetadataWhenNoKeyPresent()
533 {
534 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
535 touch($fileName);
536 $this->testFilesToDelete[] = $fileName;
537 $resource = fopen($fileName, 'r+');
538 $this->stream->attach($resource);
539
540 $expected = stream_get_meta_data($resource);
541 $test = $this->stream->getMetadata();
542
543 $this->assertEquals($expected, $test);
544 }
545
546 /**
547 * @test
548 */
549 public function getMetadataReturnsDataForSpecifiedKey()
550 {
551 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
552 touch($fileName);
553 $this->testFilesToDelete[] = $fileName;
554 $resource = fopen($fileName, 'r+');
555 $this->stream->attach($resource);
556
557 $metadata = stream_get_meta_data($resource);
558 $expected = $metadata['uri'];
559
560 $test = $this->stream->getMetadata('uri');
561
562 $this->assertEquals($expected, $test);
563 }
564
565 /**
566 * @test
567 */
568 public function getMetadataReturnsNullIfNoDataExistsForKey()
569 {
570 $fileName = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
571 touch($fileName);
572 $this->testFilesToDelete[] = $fileName;
573 $resource = fopen($fileName, 'r+');
574 $this->stream->attach($resource);
575
576 $this->assertNull($this->stream->getMetadata('TOTALLY_MADE_UP'));
577 }
578
579 /**
580 * @test
581 */
582 public function getSizeReturnsStreamSize()
583 {
584 $resource = fopen(__FILE__, 'r');
585 $expected = fstat($resource);
586 $stream = new Stream($resource);
587 $this->assertEquals($expected['size'], $stream->getSize());
588 }
589 }