[!!!][TASK] Remove deprecated methods in GeneralUtility
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Utility / GeneralUtilityTest.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Unit\Utility;
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 org\bovigo\vfs\vfsStream;
18 use org\bovigo\vfs\vfsStreamDirectory;
19 use org\bovigo\vfs\vfsStreamWrapper;
20 use TYPO3\CMS\Core\Package\Package;
21 use TYPO3\CMS\Core\Package\PackageManager;
22 use TYPO3\CMS\Core\Tests\Unit\Utility\AccessibleProxies\ExtensionManagementUtilityAccessibleProxy;
23 use TYPO3\CMS\Core\Tests\Unit\Utility\Fixtures\GeneralUtilityFilesystemFixture;
24 use TYPO3\CMS\Core\Tests\Unit\Utility\Fixtures\GeneralUtilityFixture;
25 use TYPO3\CMS\Core\Tests\Unit\Utility\Fixtures\OriginalClassFixture;
26 use TYPO3\CMS\Core\Tests\Unit\Utility\Fixtures\OtherReplacementClassFixture;
27 use TYPO3\CMS\Core\Tests\Unit\Utility\Fixtures\ReplacementClassFixture;
28 use TYPO3\CMS\Core\Tests\Unit\Utility\Fixtures\TwoParametersConstructorFixture;
29 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
30 use TYPO3\CMS\Core\Utility\GeneralUtility;
31 use TYPO3\TestingFramework\Core\FileStreamWrapper;
32
33 /**
34 * Testcase for class \TYPO3\CMS\Core\Utility\GeneralUtility
35 */
36 class GeneralUtilityTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
37 {
38 const NO_FIX_PERMISSIONS_ON_WINDOWS = 'fixPermissions() not available on Windows (method does nothing)';
39 /**
40 * @var array A backup of registered singleton instances
41 */
42 protected $singletonInstances = [];
43
44 /**
45 * @var \TYPO3\CMS\Core\Package\PackageManager
46 */
47 protected $backupPackageManager;
48
49 /**
50 * Set up
51 */
52 protected function setUp()
53 {
54 GeneralUtilityFixture::flushInternalRuntimeCaches();
55 GeneralUtilityFixture::$isAllowedHostHeaderValueCallCount = 0;
56 GeneralUtilityFixture::setAllowHostHeaderValue(false);
57 $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = GeneralUtility::ENV_TRUSTED_HOSTS_PATTERN_ALLOW_ALL;
58 $this->singletonInstances = GeneralUtility::getSingletonInstances();
59 $this->backupPackageManager = ExtensionManagementUtilityAccessibleProxy::getPackageManager();
60 }
61
62 /**
63 * Tear down
64 */
65 protected function tearDown()
66 {
67 GeneralUtility::resetSingletonInstances($this->singletonInstances);
68 ExtensionManagementUtilityAccessibleProxy::setPackageManager($this->backupPackageManager);
69 parent::tearDown();
70 }
71
72 /**
73 * Helper method to test for an existing internet connection.
74 * Some tests are skipped if there is no working uplink.
75 *
76 * @return bool $isConnected
77 */
78 public function isConnected()
79 {
80 $isConnected = false;
81 $connected = @fsockopen('typo3.org', 80);
82 if ($connected) {
83 $isConnected = true;
84 fclose($connected);
85 }
86 return $isConnected;
87 }
88
89 /**
90 * Helper method to create a random directory in the virtual file system
91 * and return the path.
92 *
93 * @param string $prefix
94 * @return string
95 */
96 protected function getVirtualTestDir($prefix = 'root_')
97 {
98 $root = vfsStream::setup();
99 $path = $root->url() . '/typo3temp/var/tests/' . $this->getUniqueId($prefix);
100 GeneralUtility::mkdir_deep($path);
101 return $path;
102 }
103
104 ///////////////////////////
105 // Tests concerning _GP
106 ///////////////////////////
107 /**
108 * @test
109 * @dataProvider gpDataProvider
110 */
111 public function canRetrieveValueWithGP($key, $get, $post, $expected)
112 {
113 $_GET = $get;
114 $_POST = $post;
115 $this->assertSame($expected, GeneralUtility::_GP($key));
116 }
117
118 /**
119 * Data provider for canRetrieveValueWithGP.
120 * All test values also check whether slashes are stripped properly.
121 *
122 * @return array
123 */
124 public function gpDataProvider()
125 {
126 return [
127 'No key parameter' => [null, [], [], null],
128 'Key not found' => ['cake', [], [], null],
129 'Value only in GET' => ['cake', ['cake' => 'li\\e'], [], 'li\\e'],
130 'Value only in POST' => ['cake', [], ['cake' => 'l\\ie'], 'l\\ie'],
131 'Value from POST preferred over GET' => ['cake', ['cake' => 'is a'], ['cake' => '\\lie'], '\\lie'],
132 'Value can be an array' => [
133 'cake',
134 ['cake' => ['is a' => 'l\\ie']],
135 [],
136 ['is a' => 'l\\ie']
137 ]
138 ];
139 }
140
141 ///////////////////////////
142 // Tests concerning _GPmerged
143 ///////////////////////////
144 /**
145 * @test
146 * @dataProvider gpMergedDataProvider
147 */
148 public function gpMergedWillMergeArraysFromGetAndPost($get, $post, $expected)
149 {
150 $_POST = $post;
151 $_GET = $get;
152 $this->assertEquals($expected, GeneralUtility::_GPmerged('cake'));
153 }
154
155 /**
156 * Data provider for gpMergedWillMergeArraysFromGetAndPost
157 *
158 * @return array
159 */
160 public function gpMergedDataProvider()
161 {
162 $fullDataArray = ['cake' => ['a' => 'is a', 'b' => 'lie']];
163 $postPartData = ['cake' => ['b' => 'lie']];
164 $getPartData = ['cake' => ['a' => 'is a']];
165 $getPartDataModified = ['cake' => ['a' => 'is not a']];
166 return [
167 'Key doesn\' exist' => [['foo'], ['bar'], []],
168 'No POST data' => [$fullDataArray, [], $fullDataArray['cake']],
169 'No GET data' => [[], $fullDataArray, $fullDataArray['cake']],
170 'POST and GET are merged' => [$getPartData, $postPartData, $fullDataArray['cake']],
171 'POST is preferred over GET' => [$getPartDataModified, $fullDataArray, $fullDataArray['cake']]
172 ];
173 }
174
175 ///////////////////////////////
176 // Tests concerning _GET / _POST
177 ///////////////////////////////
178 /**
179 * Data provider for canRetrieveGlobalInputsThroughGet
180 * and canRetrieveGlobalInputsThroughPost
181 *
182 * @return array
183 */
184 public function getAndPostDataProvider()
185 {
186 return [
187 'Requested input data doesn\'t exist' => ['cake', [], null],
188 'No key will return entire input data' => [null, ['cake' => 'l\\ie'], ['cake' => 'l\\ie']],
189 'Can retrieve specific input' => ['cake', ['cake' => 'l\\ie', 'foo'], 'l\\ie'],
190 'Can retrieve nested input data' => ['cake', ['cake' => ['is a' => 'l\\ie']], ['is a' => 'l\\ie']]
191 ];
192 }
193
194 /**
195 * @test
196 * @dataProvider getAndPostDataProvider
197 */
198 public function canRetrieveGlobalInputsThroughGet($key, $get, $expected)
199 {
200 $_GET = $get;
201 $this->assertSame($expected, GeneralUtility::_GET($key));
202 }
203
204 /**
205 * @test
206 * @dataProvider getAndPostDataProvider
207 */
208 public function canRetrieveGlobalInputsThroughPost($key, $post, $expected)
209 {
210 $_POST = $post;
211 $this->assertSame($expected, GeneralUtility::_POST($key));
212 }
213
214 ///////////////////////////////
215 // Tests concerning _GETset
216 ///////////////////////////////
217 /**
218 * @test
219 * @dataProvider getSetDataProvider
220 */
221 public function canSetNewGetInputValues($input, $key, $expected, $getPreset = [])
222 {
223 $_GET = $getPreset;
224 GeneralUtility::_GETset($input, $key);
225 $this->assertSame($expected, $_GET);
226 }
227
228 /**
229 * Data provider for canSetNewGetInputValues
230 *
231 * @return array
232 */
233 public function getSetDataProvider()
234 {
235 return [
236 'No input data used without target key' => [null, null, []],
237 'No input data used with target key' => ['', 'cake', ['cake' => '']],
238 'No target key used with string input data' => ['data', null, []],
239 'No target key used with array input data' => [['cake' => 'lie'], null, ['cake' => 'lie']],
240 'Target key and string input data' => ['lie', 'cake', ['cake' => 'lie']],
241 'Replace existing GET data' => ['lie', 'cake', ['cake' => 'lie'], ['cake' => 'is a lie']],
242 'Target key pointing to sublevels and string input data' => ['lie', 'cake|is', ['cake' => ['is' => 'lie']]],
243 'Target key pointing to sublevels and array input data' => [['a' => 'lie'], 'cake|is', ['cake' => ['is' => ['a' => 'lie']]]]
244 ];
245 }
246
247 ///////////////////////////
248 // Tests concerning cmpIPv4
249 ///////////////////////////
250 /**
251 * Data provider for cmpIPv4ReturnsTrueForMatchingAddress
252 *
253 * @return array Data sets
254 */
255 public static function cmpIPv4DataProviderMatching()
256 {
257 return [
258 'host with full IP address' => ['127.0.0.1', '127.0.0.1'],
259 'host with two wildcards at the end' => ['127.0.0.1', '127.0.*.*'],
260 'host with wildcard at third octet' => ['127.0.0.1', '127.0.*.1'],
261 'host with wildcard at second octet' => ['127.0.0.1', '127.*.0.1'],
262 '/8 subnet' => ['127.0.0.1', '127.1.1.1/8'],
263 '/32 subnet (match only name)' => ['127.0.0.1', '127.0.0.1/32'],
264 '/30 subnet' => ['10.10.3.1', '10.10.3.3/30'],
265 'host with wildcard in list with IPv4/IPv6 addresses' => ['192.168.1.1', '127.0.0.1, 1234:5678::/126, 192.168.*'],
266 'host in list with IPv4/IPv6 addresses' => ['192.168.1.1', '::1, 1234:5678::/126, 192.168.1.1'],
267 ];
268 }
269
270 /**
271 * @test
272 * @dataProvider cmpIPv4DataProviderMatching
273 */
274 public function cmpIPv4ReturnsTrueForMatchingAddress($ip, $list)
275 {
276 $this->assertTrue(GeneralUtility::cmpIPv4($ip, $list));
277 }
278
279 /**
280 * Data provider for cmpIPv4ReturnsFalseForNotMatchingAddress
281 *
282 * @return array Data sets
283 */
284 public static function cmpIPv4DataProviderNotMatching()
285 {
286 return [
287 'single host' => ['127.0.0.1', '127.0.0.2'],
288 'single host with wildcard' => ['127.0.0.1', '127.*.1.1'],
289 'single host with /32 subnet mask' => ['127.0.0.1', '127.0.0.2/32'],
290 '/31 subnet' => ['127.0.0.1', '127.0.0.2/31'],
291 'list with IPv4/IPv6 addresses' => ['127.0.0.1', '10.0.2.3, 192.168.1.1, ::1'],
292 'list with only IPv6 addresses' => ['10.20.30.40', '::1, 1234:5678::/127']
293 ];
294 }
295
296 /**
297 * @test
298 * @dataProvider cmpIPv4DataProviderNotMatching
299 */
300 public function cmpIPv4ReturnsFalseForNotMatchingAddress($ip, $list)
301 {
302 $this->assertFalse(GeneralUtility::cmpIPv4($ip, $list));
303 }
304
305 ///////////////////////////
306 // Tests concerning cmpIPv6
307 ///////////////////////////
308 /**
309 * Data provider for cmpIPv6ReturnsTrueForMatchingAddress
310 *
311 * @return array Data sets
312 */
313 public static function cmpIPv6DataProviderMatching()
314 {
315 return [
316 'empty address' => ['::', '::'],
317 'empty with netmask in list' => ['::', '::/0'],
318 'empty with netmask 0 and host-bits set in list' => ['::', '::123/0'],
319 'localhost' => ['::1', '::1'],
320 'localhost with leading zero blocks' => ['::1', '0:0::1'],
321 'host with submask /128' => ['::1', '0:0::1/128'],
322 '/16 subnet' => ['1234::1', '1234:5678::/16'],
323 '/126 subnet' => ['1234:5678::3', '1234:5678::/126'],
324 '/126 subnet with host-bits in list set' => ['1234:5678::3', '1234:5678::2/126'],
325 'list with IPv4/IPv6 addresses' => ['1234:5678::3', '::1, 127.0.0.1, 1234:5678::/126, 192.168.1.1']
326 ];
327 }
328
329 /**
330 * @test
331 * @dataProvider cmpIPv6DataProviderMatching
332 */
333 public function cmpIPv6ReturnsTrueForMatchingAddress($ip, $list)
334 {
335 $this->assertTrue(GeneralUtility::cmpIPv6($ip, $list));
336 }
337
338 /**
339 * Data provider for cmpIPv6ReturnsFalseForNotMatchingAddress
340 *
341 * @return array Data sets
342 */
343 public static function cmpIPv6DataProviderNotMatching()
344 {
345 return [
346 'empty against localhost' => ['::', '::1'],
347 'empty against localhost with /128 netmask' => ['::', '::1/128'],
348 'localhost against different host' => ['::1', '::2'],
349 'localhost against host with prior bits set' => ['::1', '::1:1'],
350 'host against different /17 subnet' => ['1234::1', '1234:f678::/17'],
351 'host against different /127 subnet' => ['1234:5678::3', '1234:5678::/127'],
352 'host against IPv4 address list' => ['1234:5678::3', '127.0.0.1, 192.168.1.1'],
353 'host against mixed list with IPv6 host in different subnet' => ['1234:5678::3', '::1, 1234:5678::/127']
354 ];
355 }
356
357 /**
358 * @test
359 * @dataProvider cmpIPv6DataProviderNotMatching
360 */
361 public function cmpIPv6ReturnsFalseForNotMatchingAddress($ip, $list)
362 {
363 $this->assertFalse(GeneralUtility::cmpIPv6($ip, $list));
364 }
365
366 ///////////////////////////////
367 // Tests concerning IPv6Hex2Bin
368 ///////////////////////////////
369 /**
370 * Data provider for IPv6Hex2BinCorrect
371 *
372 * @return array Data sets
373 */
374 public static function IPv6Hex2BinDataProviderCorrect()
375 {
376 return [
377 'empty 1' => ['::', str_pad('', 16, "\x00")],
378 'empty 2, already normalized' => ['0000:0000:0000:0000:0000:0000:0000:0000', str_pad('', 16, "\x00")],
379 'already normalized' => ['0102:0304:0000:0000:0000:0000:0506:0078', "\x01\x02\x03\x04" . str_pad('', 8, "\x00") . "\x05\x06\x00\x78"],
380 'expansion in middle 1' => ['1::2', "\x00\x01" . str_pad('', 12, "\x00") . "\x00\x02"],
381 'expansion in middle 2' => ['beef::fefa', "\xbe\xef" . str_pad('', 12, "\x00") . "\xfe\xfa"],
382 ];
383 }
384
385 /**
386 * @test
387 * @dataProvider IPv6Hex2BinDataProviderCorrect
388 */
389 public function IPv6Hex2BinCorrectlyConvertsAddresses($hex, $binary)
390 {
391 $this->assertTrue(GeneralUtility::IPv6Hex2Bin($hex) === $binary);
392 }
393
394 ///////////////////////////////
395 // Tests concerning IPv6Bin2Hex
396 ///////////////////////////////
397 /**
398 * Data provider for IPv6Bin2HexCorrect
399 *
400 * @return array Data sets
401 */
402 public static function IPv6Bin2HexDataProviderCorrect()
403 {
404 return [
405 'empty' => [str_pad('', 16, "\x00"), '::'],
406 'non-empty front' => ["\x01" . str_pad('', 15, "\x00"), '100::'],
407 'non-empty back' => [str_pad('', 15, "\x00") . "\x01", '::1'],
408 'normalized' => ["\x01\x02\x03\x04" . str_pad('', 8, "\x00") . "\x05\x06\x00\x78", '102:304::506:78'],
409 'expansion in middle 1' => ["\x00\x01" . str_pad('', 12, "\x00") . "\x00\x02", '1::2'],
410 'expansion in middle 2' => ["\xbe\xef" . str_pad('', 12, "\x00") . "\xfe\xfa", 'beef::fefa'],
411 ];
412 }
413
414 /**
415 * @test
416 * @dataProvider IPv6Bin2HexDataProviderCorrect
417 */
418 public function IPv6Bin2HexCorrectlyConvertsAddresses($binary, $hex)
419 {
420 $this->assertEquals(GeneralUtility::IPv6Bin2Hex($binary), $hex);
421 }
422
423 ////////////////////////////////////////////////
424 // Tests concerning normalizeIPv6 / compressIPv6
425 ////////////////////////////////////////////////
426 /**
427 * Data provider for normalizeIPv6ReturnsCorrectlyNormalizedFormat
428 *
429 * @return array Data sets
430 */
431 public static function normalizeCompressIPv6DataProviderCorrect()
432 {
433 return [
434 'empty' => ['::', '0000:0000:0000:0000:0000:0000:0000:0000'],
435 'localhost' => ['::1', '0000:0000:0000:0000:0000:0000:0000:0001'],
436 'expansion in middle 1' => ['1::2', '0001:0000:0000:0000:0000:0000:0000:0002'],
437 'expansion in middle 2' => ['1:2::3', '0001:0002:0000:0000:0000:0000:0000:0003'],
438 'expansion in middle 3' => ['1::2:3', '0001:0000:0000:0000:0000:0000:0002:0003'],
439 'expansion in middle 4' => ['1:2::3:4:5', '0001:0002:0000:0000:0000:0003:0004:0005']
440 ];
441 }
442
443 /**
444 * @test
445 * @dataProvider normalizeCompressIPv6DataProviderCorrect
446 */
447 public function normalizeIPv6CorrectlyNormalizesAddresses($compressed, $normalized)
448 {
449 $this->assertEquals($normalized, GeneralUtility::normalizeIPv6($compressed));
450 }
451
452 /**
453 * @test
454 * @dataProvider normalizeCompressIPv6DataProviderCorrect
455 */
456 public function compressIPv6CorrectlyCompressesAdresses($compressed, $normalized)
457 {
458 $this->assertEquals($compressed, GeneralUtility::compressIPv6($normalized));
459 }
460
461 /**
462 * @test
463 */
464 public function compressIPv6CorrectlyCompressesAdressWithSomeAddressOnRightSide()
465 {
466 if (strtolower(PHP_OS) === 'darwin') {
467 $this->markTestSkipped('This test does not work on OSX / Darwin OS.');
468 }
469 $this->assertEquals('::f0f', GeneralUtility::compressIPv6('0000:0000:0000:0000:0000:0000:0000:0f0f'));
470 }
471
472 ///////////////////////////////
473 // Tests concerning validIP
474 ///////////////////////////////
475 /**
476 * Data provider for checkValidIpReturnsTrueForValidIp
477 *
478 * @return array Data sets
479 */
480 public static function validIpDataProvider()
481 {
482 return [
483 '0.0.0.0' => ['0.0.0.0'],
484 'private IPv4 class C' => ['192.168.0.1'],
485 'private IPv4 class A' => ['10.0.13.1'],
486 'private IPv6' => ['fe80::daa2:5eff:fe8b:7dfb']
487 ];
488 }
489
490 /**
491 * @test
492 * @dataProvider validIpDataProvider
493 */
494 public function validIpReturnsTrueForValidIp($ip)
495 {
496 $this->assertTrue(GeneralUtility::validIP($ip));
497 }
498
499 /**
500 * Data provider for checkValidIpReturnsFalseForInvalidIp
501 *
502 * @return array Data sets
503 */
504 public static function invalidIpDataProvider()
505 {
506 return [
507 'null' => [null],
508 'zero' => [0],
509 'string' => ['test'],
510 'string empty' => [''],
511 'string NULL' => ['NULL'],
512 'out of bounds IPv4' => ['300.300.300.300'],
513 'dotted decimal notation with only two dots' => ['127.0.1']
514 ];
515 }
516
517 /**
518 * @test
519 * @dataProvider invalidIpDataProvider
520 */
521 public function validIpReturnsFalseForInvalidIp($ip)
522 {
523 $this->assertFalse(GeneralUtility::validIP($ip));
524 }
525
526 ///////////////////////////////
527 // Tests concerning cmpFQDN
528 ///////////////////////////////
529 /**
530 * Data provider for cmpFqdnReturnsTrue
531 *
532 * @return array Data sets
533 */
534 public static function cmpFqdnValidDataProvider()
535 {
536 return [
537 'localhost should usually resolve, IPv4' => ['127.0.0.1', '*'],
538 'localhost should usually resolve, IPv6' => ['::1', '*'],
539 // other testcases with resolving not possible since it would
540 // require a working IPv4/IPv6-connectivity
541 'aaa.bbb.ccc.ddd.eee, full' => ['aaa.bbb.ccc.ddd.eee', 'aaa.bbb.ccc.ddd.eee'],
542 'aaa.bbb.ccc.ddd.eee, wildcard first' => ['aaa.bbb.ccc.ddd.eee', '*.ccc.ddd.eee'],
543 'aaa.bbb.ccc.ddd.eee, wildcard last' => ['aaa.bbb.ccc.ddd.eee', 'aaa.bbb.ccc.*'],
544 'aaa.bbb.ccc.ddd.eee, wildcard middle' => ['aaa.bbb.ccc.ddd.eee', 'aaa.*.eee'],
545 'list-matches, 1' => ['aaa.bbb.ccc.ddd.eee', 'xxx, yyy, zzz, aaa.*.eee'],
546 'list-matches, 2' => ['aaa.bbb.ccc.ddd.eee', '127:0:0:1,,aaa.*.eee,::1']
547 ];
548 }
549
550 /**
551 * @test
552 * @dataProvider cmpFqdnValidDataProvider
553 */
554 public function cmpFqdnReturnsTrue($baseHost, $list)
555 {
556 $this->assertTrue(GeneralUtility::cmpFQDN($baseHost, $list));
557 }
558
559 /**
560 * Data provider for cmpFqdnReturnsFalse
561 *
562 * @return array Data sets
563 */
564 public static function cmpFqdnInvalidDataProvider()
565 {
566 return [
567 'num-parts of hostname to check can only be less or equal than hostname, 1' => ['aaa.bbb.ccc.ddd.eee', 'aaa.bbb.ccc.ddd.eee.fff'],
568 'num-parts of hostname to check can only be less or equal than hostname, 2' => ['aaa.bbb.ccc.ddd.eee', 'aaa.*.bbb.ccc.ddd.eee']
569 ];
570 }
571
572 /**
573 * @test
574 * @dataProvider cmpFqdnInvalidDataProvider
575 */
576 public function cmpFqdnReturnsFalse($baseHost, $list)
577 {
578 $this->assertFalse(GeneralUtility::cmpFQDN($baseHost, $list));
579 }
580
581 ///////////////////////////////
582 // Tests concerning inList
583 ///////////////////////////////
584 /**
585 * @test
586 * @param string $haystack
587 * @dataProvider inListForItemContainedReturnsTrueDataProvider
588 */
589 public function inListForItemContainedReturnsTrue($haystack)
590 {
591 $this->assertTrue(GeneralUtility::inList($haystack, 'findme'));
592 }
593
594 /**
595 * Data provider for inListForItemContainedReturnsTrue.
596 *
597 * @return array
598 */
599 public function inListForItemContainedReturnsTrueDataProvider()
600 {
601 return [
602 'Element as second element of four items' => ['one,findme,three,four'],
603 'Element at beginning of list' => ['findme,one,two'],
604 'Element at end of list' => ['one,two,findme'],
605 'One item list' => ['findme']
606 ];
607 }
608
609 /**
610 * @test
611 * @param string $haystack
612 * @dataProvider inListForItemNotContainedReturnsFalseDataProvider
613 */
614 public function inListForItemNotContainedReturnsFalse($haystack)
615 {
616 $this->assertFalse(GeneralUtility::inList($haystack, 'findme'));
617 }
618
619 /**
620 * Data provider for inListForItemNotContainedReturnsFalse.
621 *
622 * @return array
623 */
624 public function inListForItemNotContainedReturnsFalseDataProvider()
625 {
626 return [
627 'Four item list' => ['one,two,three,four'],
628 'One item list' => ['one'],
629 'Empty list' => ['']
630 ];
631 }
632
633 ///////////////////////////////
634 // Tests concerning rmFromList
635 ///////////////////////////////
636 /**
637 * @test
638 * @param string $initialList
639 * @param string $listWithElementRemoved
640 * @dataProvider rmFromListRemovesElementsFromCommaSeparatedListDataProvider
641 */
642 public function rmFromListRemovesElementsFromCommaSeparatedList($initialList, $listWithElementRemoved)
643 {
644 $this->assertSame($listWithElementRemoved, GeneralUtility::rmFromList('removeme', $initialList));
645 }
646
647 /**
648 * Data provider for rmFromListRemovesElementsFromCommaSeparatedList
649 *
650 * @return array
651 */
652 public function rmFromListRemovesElementsFromCommaSeparatedListDataProvider()
653 {
654 return [
655 'Element as second element of three' => ['one,removeme,two', 'one,two'],
656 'Element at beginning of list' => ['removeme,one,two', 'one,two'],
657 'Element at end of list' => ['one,two,removeme', 'one,two'],
658 'One item list' => ['removeme', ''],
659 'Element not contained in list' => ['one,two,three', 'one,two,three'],
660 'Empty element survives' => ['one,,three,,removeme', 'one,,three,'],
661 'Empty element survives at start' => [',removeme,three,removeme', ',three'],
662 'Empty element survives at end' => ['removeme,three,removeme,', 'three,'],
663 'Empty list' => ['', ''],
664 'List contains removeme multiple times' => ['removeme,notme,removeme,removeme', 'notme'],
665 'List contains removeme multiple times nothing else' => ['removeme,removeme,removeme', ''],
666 'List contains removeme multiple times nothing else 2x' => ['removeme,removeme', ''],
667 'List contains removeme multiple times nothing else 3x' => ['removeme,removeme,removeme', ''],
668 'List contains removeme multiple times nothing else 4x' => ['removeme,removeme,removeme,removeme', ''],
669 'List contains removeme multiple times nothing else 5x' => ['removeme,removeme,removeme,removeme,removeme', ''],
670 ];
671 }
672
673 ///////////////////////////////
674 // Tests concerning expandList
675 ///////////////////////////////
676 /**
677 * @test
678 * @param string $list
679 * @param string $expectation
680 * @dataProvider expandListExpandsIntegerRangesDataProvider
681 */
682 public function expandListExpandsIntegerRanges($list, $expectation)
683 {
684 $this->assertSame($expectation, GeneralUtility::expandList($list));
685 }
686
687 /**
688 * Data provider for expandListExpandsIntegerRangesDataProvider
689 *
690 * @return array
691 */
692 public function expandListExpandsIntegerRangesDataProvider()
693 {
694 return [
695 'Expand for the same number' => ['1,2-2,7', '1,2,7'],
696 'Small range expand with parameters reversed ignores reversed items' => ['1,5-3,7', '1,7'],
697 'Small range expand' => ['1,3-5,7', '1,3,4,5,7'],
698 'Expand at beginning' => ['3-5,1,7', '3,4,5,1,7'],
699 'Expand at end' => ['1,7,3-5', '1,7,3,4,5'],
700 'Multiple small range expands' => ['1,3-5,7-10,12', '1,3,4,5,7,8,9,10,12'],
701 'One item list' => ['1-5', '1,2,3,4,5'],
702 'Nothing to expand' => ['1,2,3,4', '1,2,3,4'],
703 'Empty list' => ['', '']
704 ];
705 }
706
707 /**
708 * @test
709 */
710 public function expandListExpandsForTwoThousandElementsExpandsOnlyToThousandElementsMaximum()
711 {
712 $list = GeneralUtility::expandList('1-2000');
713 $this->assertSame(1000, count(explode(',', $list)));
714 }
715
716 ///////////////////////////////
717 // Tests concerning uniqueList
718 ///////////////////////////////
719 /**
720 * @test
721 * @param string $initialList
722 * @param string $unifiedList
723 * @dataProvider uniqueListUnifiesCommaSeparatedListDataProvider
724 */
725 public function uniqueListUnifiesCommaSeparatedList($initialList, $unifiedList)
726 {
727 $this->assertSame($unifiedList, GeneralUtility::uniqueList($initialList));
728 }
729
730 /**
731 * Data provider for uniqueListUnifiesCommaSeparatedList
732 *
733 * @return array
734 */
735 public function uniqueListUnifiesCommaSeparatedListDataProvider()
736 {
737 return [
738 'List without duplicates' => ['one,two,three', 'one,two,three'],
739 'List with two consecutive duplicates' => ['one,two,two,three,three', 'one,two,three'],
740 'List with non-consecutive duplicates' => ['one,two,three,two,three', 'one,two,three'],
741 'One item list' => ['one', 'one'],
742 'Empty list' => ['', '']
743 ];
744 }
745
746 ///////////////////////////////
747 // Tests concerning isFirstPartOfStr
748 ///////////////////////////////
749 /**
750 * Data provider for isFirstPartOfStrReturnsTrueForMatchingFirstParts
751 *
752 * @return array
753 */
754 public function isFirstPartOfStrReturnsTrueForMatchingFirstPartDataProvider()
755 {
756 return [
757 'match first part of string' => ['hello world', 'hello'],
758 'match whole string' => ['hello', 'hello'],
759 'integer is part of string with same number' => ['24', 24],
760 'string is part of integer with same number' => [24, '24'],
761 'integer is part of string starting with same number' => ['24 beer please', 24]
762 ];
763 }
764
765 /**
766 * @test
767 * @dataProvider isFirstPartOfStrReturnsTrueForMatchingFirstPartDataProvider
768 */
769 public function isFirstPartOfStrReturnsTrueForMatchingFirstPart($string, $part)
770 {
771 $this->assertTrue(GeneralUtility::isFirstPartOfStr($string, $part));
772 }
773
774 /**
775 * Data provider for checkIsFirstPartOfStrReturnsFalseForNotMatchingFirstParts
776 *
777 * @return array
778 */
779 public function isFirstPartOfStrReturnsFalseForNotMatchingFirstPartDataProvider()
780 {
781 return [
782 'no string match' => ['hello', 'bye'],
783 'no case sensitive string match' => ['hello world', 'Hello'],
784 'array is not part of string' => ['string', []],
785 'string is not part of array' => [[], 'string'],
786 'NULL is not part of string' => ['string', null],
787 'string is not part of NULL' => [null, 'string'],
788 'NULL is not part of array' => [[], null],
789 'array is not part of NULL' => [null, []],
790 'empty string is not part of empty string' => ['', ''],
791 'NULL is not part of empty string' => ['', null],
792 'false is not part of empty string' => ['', false],
793 'empty string is not part of NULL' => [null, ''],
794 'empty string is not part of false' => [false, ''],
795 'empty string is not part of zero integer' => [0, ''],
796 'zero integer is not part of NULL' => [null, 0],
797 'zero integer is not part of empty string' => ['', 0]
798 ];
799 }
800
801 /**
802 * @test
803 * @dataProvider isFirstPartOfStrReturnsFalseForNotMatchingFirstPartDataProvider
804 */
805 public function isFirstPartOfStrReturnsFalseForNotMatchingFirstPart($string, $part)
806 {
807 $this->assertFalse(GeneralUtility::isFirstPartOfStr($string, $part));
808 }
809
810 ///////////////////////////////
811 // Tests concerning formatSize
812 ///////////////////////////////
813 /**
814 * @test
815 * @dataProvider formatSizeDataProvider
816 */
817 public function formatSizeTranslatesBytesToHigherOrderRepresentation($size, $labels, $base, $expected)
818 {
819 $this->assertEquals($expected, GeneralUtility::formatSize($size, $labels, $base));
820 }
821
822 /**
823 * Data provider for formatSizeTranslatesBytesToHigherOrderRepresentation
824 *
825 * @return array
826 */
827 public function formatSizeDataProvider()
828 {
829 return [
830 'IEC Bytes stay bytes (min)' => [1, '', 0, '1 '],
831 'IEC Bytes stay bytes (max)' => [921, '', 0, '921 '],
832 'IEC Kilobytes are used (min)' => [922, '', 0, '0.90 Ki'],
833 'IEC Kilobytes are used (max)' => [943718, '', 0, '922 Ki'],
834 'IEC Megabytes are used (min)' => [943719, '', 0, '0.90 Mi'],
835 'IEC Megabytes are used (max)' => [966367641, '', 0, '922 Mi'],
836 'IEC Gigabytes are used (min)' => [966367642, '', 0, '0.90 Gi'],
837 'IEC Gigabytes are used (max)' => [989560464998, '', 0, '922 Gi'],
838 'IEC Decimal is omitted for large kilobytes' => [31080, '', 0, '30 Ki'],
839 'IEC Decimal is omitted for large megabytes' => [31458000, '', 0, '30 Mi'],
840 'IEC Decimal is omitted for large gigabytes' => [32212254720, '', 0, '30 Gi'],
841 'SI Bytes stay bytes (min)' => [1, 'si', 0, '1 '],
842 'SI Bytes stay bytes (max)' => [899, 'si', 0, '899 '],
843 'SI Kilobytes are used (min)' => [901, 'si', 0, '0.90 k'],
844 'SI Kilobytes are used (max)' => [900000, 'si', 0, '900 k'],
845 'SI Megabytes are used (min)' => [900001, 'si', 0, '0.90 M'],
846 'SI Megabytes are used (max)' => [900000000, 'si', 0, '900 M'],
847 'SI Gigabytes are used (min)' => [900000001, 'si', 0, '0.90 G'],
848 'SI Gigabytes are used (max)' => [900000000000, 'si', 0, '900 G'],
849 'SI Decimal is omitted for large kilobytes' => [30000, 'si', 0, '30 k'],
850 'SI Decimal is omitted for large megabytes' => [30000000, 'si', 0, '30 M'],
851 'SI Decimal is omitted for large gigabytes' => [30000000000, 'si', 0, '30 G'],
852 'Label for bytes can be exchanged (binary unit)' => [1, ' Foo|||', 0, '1 Foo'],
853 'Label for kilobytes can be exchanged (binary unit)' => [1024, '| Foo||', 0, '1.00 Foo'],
854 'Label for megabyes can be exchanged (binary unit)' => [1048576, '|| Foo|', 0, '1.00 Foo'],
855 'Label for gigabytes can be exchanged (binary unit)' => [1073741824, '||| Foo', 0, '1.00 Foo'],
856 'Label for bytes can be exchanged (decimal unit)' => [1, ' Foo|||', 1000, '1 Foo'],
857 'Label for kilobytes can be exchanged (decimal unit)' => [1000, '| Foo||', 1000, '1.00 Foo'],
858 'Label for megabyes can be exchanged (decimal unit)' => [1000000, '|| Foo|', 1000, '1.00 Foo'],
859 'Label for gigabytes can be exchanged (decimal unit)' => [1000000000, '||| Foo', 1000, '1.00 Foo'],
860 'IEC Base is ignored' => [1024, 'iec', 1000, '1.00 Ki'],
861 'SI Base is ignored' => [1000, 'si', 1024, '1.00 k'],
862 'Use binary base for unexpected base' => [2048, '| Bar||', 512, '2.00 Bar']
863 ];
864 }
865
866 ///////////////////////////////
867 // Tests concerning splitCalc
868 ///////////////////////////////
869 /**
870 * Data provider for splitCalc
871 *
872 * @return array expected values, arithmetic expression
873 */
874 public function splitCalcDataProvider()
875 {
876 return [
877 'empty string returns empty array' => [
878 [],
879 ''
880 ],
881 'number without operator returns array with plus and number' => [
882 [['+', 42]],
883 '42'
884 ],
885 'two numbers with asterisk return first number with plus and second number with asterisk' => [
886 [['+', 42], ['*', 31]],
887 '42 * 31'
888 ]
889 ];
890 }
891
892 /**
893 * @test
894 * @dataProvider splitCalcDataProvider
895 */
896 public function splitCalcCorrectlySplitsExpression($expected, $expression)
897 {
898 $this->assertEquals($expected, GeneralUtility::splitCalc($expression, '+-*/'));
899 }
900
901 ///////////////////////////////
902 // Tests concerning htmlspecialchars_decode
903 ///////////////////////////////
904 /**
905 * @test
906 */
907 public function htmlspecialcharsDecodeReturnsDecodedString()
908 {
909 $string = '<typo3 version="6.0">&nbsp;</typo3>';
910 $encoded = htmlspecialchars($string);
911 $decoded = htmlspecialchars_decode($encoded);
912 $this->assertEquals($string, $decoded);
913 }
914
915 //////////////////////////////////
916 // Tests concerning validEmail
917 //////////////////////////////////
918 /**
919 * Data provider for valid validEmail's
920 *
921 * @return array Valid email addresses
922 */
923 public function validEmailValidDataProvider()
924 {
925 return [
926 'short mail address' => ['a@b.c'],
927 'simple mail address' => ['test@example.com'],
928 'uppercase characters' => ['QWERTYUIOPASDFGHJKLZXCVBNM@QWERTYUIOPASDFGHJKLZXCVBNM.NET'],
929 'equal sign in local part' => ['test=mail@example.com'],
930 'dash in local part' => ['test-mail@example.com'],
931 'plus in local part' => ['test+mail@example.com'],
932 'question mark in local part' => ['test?mail@example.com'],
933 'slash in local part' => ['foo/bar@example.com'],
934 'hash in local part' => ['foo#bar@example.com'],
935 'dot in local part' => ['firstname.lastname@employee.2something.com'],
936 'dash as local part' => ['-@foo.com'],
937 'umlauts in domain part' => ['foo@äöüfoo.com']
938 ];
939 }
940
941 /**
942 * @test
943 * @dataProvider validEmailValidDataProvider
944 */
945 public function validEmailReturnsTrueForValidMailAddress($address)
946 {
947 $this->assertTrue(GeneralUtility::validEmail($address));
948 }
949
950 /**
951 * Data provider for invalid validEmail's
952 *
953 * @return array Invalid email addresses
954 */
955 public function validEmailInvalidDataProvider()
956 {
957 return [
958 'empty string' => [''],
959 'empty array' => [[]],
960 'integer' => [42],
961 'float' => [42.23],
962 'array' => [['foo']],
963 'object' => [new \stdClass()],
964 '@ sign only' => ['@'],
965 'string longer than 320 characters' => [str_repeat('0123456789', 33)],
966 'duplicate @' => ['test@@example.com'],
967 'duplicate @ combined with further special characters in local part' => ['test!.!@#$%^&*@example.com'],
968 'opening parenthesis in local part' => ['foo(bar@example.com'],
969 'closing parenthesis in local part' => ['foo)bar@example.com'],
970 'opening square bracket in local part' => ['foo[bar@example.com'],
971 'closing square bracket as local part' => [']@example.com'],
972 'top level domain only' => ['test@com'],
973 'dash as second level domain' => ['foo@-.com'],
974 'domain part starting with dash' => ['foo@-foo.com'],
975 'domain part ending with dash' => ['foo@foo-.com'],
976 'number as top level domain' => ['foo@bar.123'],
977 'dot at beginning of domain part' => ['test@.com'],
978 'local part ends with dot' => ['e.x.a.m.p.l.e.@example.com'],
979 'umlauts in local part' => ['äöüfoo@bar.com'],
980 'trailing whitespace' => ['test@example.com '],
981 'trailing carriage return' => ['test@example.com' . CR],
982 'trailing linefeed' => ['test@example.com' . LF],
983 'trailing carriage return linefeed' => ['test@example.com' . CRLF],
984 'trailing tab' => ['test@example.com' . TAB]
985 ];
986 }
987
988 /**
989 * @test
990 * @dataProvider validEmailInvalidDataProvider
991 */
992 public function validEmailReturnsFalseForInvalidMailAddress($address)
993 {
994 $this->assertFalse(GeneralUtility::validEmail($address));
995 }
996
997 //////////////////////////////////
998 // Tests concerning intExplode
999 //////////////////////////////////
1000 /**
1001 * @test
1002 */
1003 public function intExplodeConvertsStringsToInteger()
1004 {
1005 $testString = '1,foo,2';
1006 $expectedArray = [1, 0, 2];
1007 $actualArray = GeneralUtility::intExplode(',', $testString);
1008 $this->assertEquals($expectedArray, $actualArray);
1009 }
1010
1011 //////////////////////////////////
1012 // Tests concerning implodeArrayForUrl / explodeUrl2Array
1013 //////////////////////////////////
1014 /**
1015 * Data provider for implodeArrayForUrlBuildsValidParameterString and
1016 * explodeUrl2ArrayTransformsParameterStringToArray
1017 *
1018 * @return array
1019 */
1020 public function implodeArrayForUrlDataProvider()
1021 {
1022 $valueArray = ['one' => '√', 'two' => 2];
1023 return [
1024 'Empty input' => ['foo', [], ''],
1025 'String parameters' => ['foo', $valueArray, '&foo[one]=%E2%88%9A&foo[two]=2'],
1026 'Nested array parameters' => ['foo', [$valueArray], '&foo[0][one]=%E2%88%9A&foo[0][two]=2'],
1027 'Keep blank parameters' => ['foo', ['one' => '√', ''], '&foo[one]=%E2%88%9A&foo[0]=']
1028 ];
1029 }
1030
1031 /**
1032 * @test
1033 * @dataProvider implodeArrayForUrlDataProvider
1034 */
1035 public function implodeArrayForUrlBuildsValidParameterString($name, $input, $expected)
1036 {
1037 $this->assertSame($expected, GeneralUtility::implodeArrayForUrl($name, $input));
1038 }
1039
1040 /**
1041 * @test
1042 */
1043 public function implodeArrayForUrlCanSkipEmptyParameters()
1044 {
1045 $input = ['one' => '√', ''];
1046 $expected = '&foo[one]=%E2%88%9A';
1047 $this->assertSame($expected, GeneralUtility::implodeArrayForUrl('foo', $input, '', true));
1048 }
1049
1050 /**
1051 * @test
1052 */
1053 public function implodeArrayForUrlCanUrlEncodeKeyNames()
1054 {
1055 $input = ['one' => '√', ''];
1056 $expected = '&foo%5Bone%5D=%E2%88%9A&foo%5B0%5D=';
1057 $this->assertSame($expected, GeneralUtility::implodeArrayForUrl('foo', $input, '', false, true));
1058 }
1059
1060 /**
1061 * @test
1062 * @dataProvider implodeArrayForUrlDataProvider
1063 */
1064 public function explodeUrl2ArrayTransformsParameterStringToNestedArray($name, $array, $input)
1065 {
1066 $expected = $array ? [$name => $array] : [];
1067 $this->assertEquals($expected, GeneralUtility::explodeUrl2Array($input, true));
1068 }
1069
1070 /**
1071 * @test
1072 * @dataProvider explodeUrl2ArrayDataProvider
1073 */
1074 public function explodeUrl2ArrayTransformsParameterStringToFlatArray($input, $expected)
1075 {
1076 $this->assertEquals($expected, GeneralUtility::explodeUrl2Array($input, false));
1077 }
1078
1079 /**
1080 * Data provider for explodeUrl2ArrayTransformsParameterStringToFlatArray
1081 *
1082 * @return array
1083 */
1084 public function explodeUrl2ArrayDataProvider()
1085 {
1086 return [
1087 'Empty string' => ['', []],
1088 'Simple parameter string' => ['&one=%E2%88%9A&two=2', ['one' => '√', 'two' => 2]],
1089 'Nested parameter string' => ['&foo[one]=%E2%88%9A&two=2', ['foo[one]' => '√', 'two' => 2]]
1090 ];
1091 }
1092
1093 //////////////////////////////////
1094 // Tests concerning compileSelectedGetVarsFromArray
1095 //////////////////////////////////
1096 /**
1097 * @test
1098 */
1099 public function compileSelectedGetVarsFromArrayFiltersIncomingData()
1100 {
1101 $filter = 'foo,bar';
1102 $getArray = ['foo' => 1, 'cake' => 'lie'];
1103 $expected = ['foo' => 1];
1104 $result = GeneralUtility::compileSelectedGetVarsFromArray($filter, $getArray, false);
1105 $this->assertSame($expected, $result);
1106 }
1107
1108 /**
1109 * @test
1110 */
1111 public function compileSelectedGetVarsFromArrayUsesGetPostDataFallback()
1112 {
1113 $_GET['bar'] = '2';
1114 $filter = 'foo,bar';
1115 $getArray = ['foo' => 1, 'cake' => 'lie'];
1116 $expected = ['foo' => 1, 'bar' => '2'];
1117 $result = GeneralUtility::compileSelectedGetVarsFromArray($filter, $getArray, true);
1118 $this->assertSame($expected, $result);
1119 }
1120
1121 //////////////////////////////////
1122 // Tests concerning revExplode
1123 //////////////////////////////////
1124
1125 /**
1126 * @return array
1127 */
1128 public function revExplodeDataProvider()
1129 {
1130 return [
1131 'limit 0 should return unexploded string' => [
1132 ':',
1133 'my:words:here',
1134 0,
1135 ['my:words:here']
1136 ],
1137 'limit 1 should return unexploded string' => [
1138 ':',
1139 'my:words:here',
1140 1,
1141 ['my:words:here']
1142 ],
1143 'limit 2 should return two pieces' => [
1144 ':',
1145 'my:words:here',
1146 2,
1147 ['my:words', 'here']
1148 ],
1149 'limit 3 should return unexploded string' => [
1150 ':',
1151 'my:words:here',
1152 3,
1153 ['my', 'words', 'here']
1154 ],
1155 'limit 0 should return unexploded string if no delimiter is contained' => [
1156 ':',
1157 'mywordshere',
1158 0,
1159 ['mywordshere']
1160 ],
1161 'limit 1 should return unexploded string if no delimiter is contained' => [
1162 ':',
1163 'mywordshere',
1164 1,
1165 ['mywordshere']
1166 ],
1167 'limit 2 should return unexploded string if no delimiter is contained' => [
1168 ':',
1169 'mywordshere',
1170 2,
1171 ['mywordshere']
1172 ],
1173 'limit 3 should return unexploded string if no delimiter is contained' => [
1174 ':',
1175 'mywordshere',
1176 3,
1177 ['mywordshere']
1178 ],
1179 'multi character delimiter is handled properly with limit 2' => [
1180 '[]',
1181 'a[b][c][d]',
1182 2,
1183 ['a[b][c', 'd]']
1184 ],
1185 'multi character delimiter is handled properly with limit 3' => [
1186 '[]',
1187 'a[b][c][d]',
1188 3,
1189 ['a[b', 'c', 'd]']
1190 ],
1191 ];
1192 }
1193
1194 /**
1195 * @test
1196 * @dataProvider revExplodeDataProvider
1197 */
1198 public function revExplodeCorrectlyExplodesStringForGivenPartsCount($delimiter, $testString, $count, $expectedArray)
1199 {
1200 $actualArray = GeneralUtility::revExplode($delimiter, $testString, $count);
1201 $this->assertEquals($expectedArray, $actualArray);
1202 }
1203
1204 /**
1205 * @test
1206 */
1207 public function revExplodeRespectsLimitThreeWhenExploding()
1208 {
1209 $testString = 'even:more:of:my:words:here';
1210 $expectedArray = ['even:more:of:my', 'words', 'here'];
1211 $actualArray = GeneralUtility::revExplode(':', $testString, 3);
1212 $this->assertEquals($expectedArray, $actualArray);
1213 }
1214
1215 //////////////////////////////////
1216 // Tests concerning trimExplode
1217 //////////////////////////////////
1218 /**
1219 * @test
1220 * @dataProvider trimExplodeReturnsCorrectResultDataProvider
1221 *
1222 * @param string $delimiter
1223 * @param string $testString
1224 * @param bool $removeEmpty
1225 * @param int $limit
1226 * @param array $expectedResult
1227 */
1228 public function trimExplodeReturnsCorrectResult($delimiter, $testString, $removeEmpty, $limit, $expectedResult)
1229 {
1230 $this->assertSame($expectedResult, GeneralUtility::trimExplode($delimiter, $testString, $removeEmpty, $limit));
1231 }
1232
1233 /**
1234 * @return array
1235 */
1236 public function trimExplodeReturnsCorrectResultDataProvider()
1237 {
1238 return [
1239 'spaces at element start and end' => [
1240 ',',
1241 ' a , b , c ,d ,, e,f,',
1242 false,
1243 0,
1244 ['a', 'b', 'c', 'd', '', 'e', 'f', '']
1245 ],
1246 'removes newline' => [
1247 ',',
1248 ' a , b , ' . LF . ' ,d ,, e,f,',
1249 true,
1250 0,
1251 ['a', 'b', 'd', 'e', 'f']
1252 ],
1253 'removes empty elements' => [
1254 ',',
1255 'a , b , c , ,d ,, ,e,f,',
1256 true,
1257 0,
1258 ['a', 'b', 'c', 'd', 'e', 'f']
1259 ],
1260 'keeps remaining results with empty items after reaching limit with positive parameter' => [
1261 ',',
1262 ' a , b , c , , d,, ,e ',
1263 false,
1264 3,
1265 ['a', 'b', 'c , , d,, ,e']
1266 ],
1267 'keeps remaining results without empty items after reaching limit with positive parameter' => [
1268 ',',
1269 ' a , b , c , , d,, ,e ',
1270 true,
1271 3,
1272 ['a', 'b', 'c , d,e']
1273 ],
1274 'keeps remaining results with empty items after reaching limit with negative parameter' => [
1275 ',',
1276 ' a , b , c , d, ,e, f , , ',
1277 false,
1278 -3,
1279 ['a', 'b', 'c', 'd', '', 'e']
1280 ],
1281 'keeps remaining results without empty items after reaching limit with negative parameter' => [
1282 ',',
1283 ' a , b , c , d, ,e, f , , ',
1284 true,
1285 -3,
1286 ['a', 'b', 'c']
1287 ],
1288 'returns exact results without reaching limit with positive parameter' => [
1289 ',',
1290 ' a , b , , c , , , ',
1291 true,
1292 4,
1293 ['a', 'b', 'c']
1294 ],
1295 'keeps zero as string' => [
1296 ',',
1297 'a , b , c , ,d ,, ,e,f, 0 ,',
1298 true,
1299 0,
1300 ['a', 'b', 'c', 'd', 'e', 'f', '0']
1301 ],
1302 'keeps whitespace inside elements' => [
1303 ',',
1304 'a , b , c , ,d ,, ,e,f, g h ,',
1305 true,
1306 0,
1307 ['a', 'b', 'c', 'd', 'e', 'f', 'g h']
1308 ],
1309 'can use internal regex delimiter as explode delimiter' => [
1310 '/',
1311 'a / b / c / /d // /e/f/ g h /',
1312 true,
1313 0,
1314 ['a', 'b', 'c', 'd', 'e', 'f', 'g h']
1315 ],
1316 'can use whitespaces as delimiter' => [
1317 ' ',
1318 '* * * * *',
1319 true,
1320 0,
1321 ['*', '*', '*', '*', '*']
1322 ],
1323 'can use words as delimiter' => [
1324 'All',
1325 'HelloAllTogether',
1326 true,
1327 0,
1328 ['Hello', 'Together']
1329 ],
1330 'can use word with appended and prepended spaces as delimiter' => [
1331 ' all ',
1332 'Hello all together',
1333 true,
1334 0,
1335 ['Hello', 'together']
1336 ],
1337 'can use word with appended and prepended spaces as delimiter and do not remove empty' => [
1338 ' all ',
1339 'Hello all together all there all all are all none',
1340 false,
1341 0,
1342 ['Hello', 'together', 'there', '', 'are', 'none']
1343 ],
1344 'can use word with appended and prepended spaces as delimiter, do not remove empty and limit' => [
1345 ' all ',
1346 'Hello all together all there all all are all none',
1347 false,
1348 5,
1349 ['Hello', 'together', 'there', '', 'are all none']
1350 ],
1351 'can use word with appended and prepended spaces as delimiter, do not remove empty, limit and multiple delimiter in last' => [
1352 ' all ',
1353 'Hello all together all there all all are all none',
1354 false,
1355 4,
1356 ['Hello', 'together', 'there', 'all are all none']
1357 ],
1358 'can use word with appended and prepended spaces as delimiter, remove empty and limit' => [
1359 ' all ',
1360 'Hello all together all there all all are all none',
1361 true,
1362 4,
1363 ['Hello', 'together', 'there', 'are all none']
1364 ],
1365 'can use word with appended and prepended spaces as delimiter, remove empty and limit and multiple delimiter in last' => [
1366 ' all ',
1367 'Hello all together all there all all are all none',
1368 true,
1369 5,
1370 ['Hello', 'together', 'there', 'are' , 'none']
1371 ],
1372 'can use words as delimiter and do not remove empty' => [
1373 'all there',
1374 'Helloall theretogether all there all there are all there none',
1375 false,
1376 0,
1377 ['Hello', 'together', '', 'are', 'none']
1378 ],
1379 'can use words as delimiter, do not remove empty and limit' => [
1380 'all there',
1381 'Helloall theretogether all there all there are all there none',
1382 false,
1383 4,
1384 ['Hello', 'together', '', 'are all there none']
1385 ],
1386 'can use words as delimiter, do not remove empty, limit and multiple delimiter in last' => [
1387 'all there',
1388 'Helloall theretogether all there all there are all there none',
1389 false,
1390 3,
1391 ['Hello', 'together', 'all there are all there none']
1392 ],
1393 'can use words as delimiter, remove empty' => [
1394 'all there',
1395 'Helloall theretogether all there all there are all there none',
1396 true,
1397 0,
1398 ['Hello', 'together', 'are', 'none']
1399 ],
1400 'can use words as delimiter, remove empty and limit' => [
1401 'all there',
1402 'Helloall theretogether all there all there are all there none',
1403 true,
1404 3,
1405 ['Hello', 'together', 'are all there none']
1406 ],
1407 'can use words as delimiter, remove empty and limit and multiple delimiter in last' => [
1408 'all there',
1409 'Helloall theretogether all there all there are all there none',
1410 true,
1411 4,
1412 ['Hello', 'together', 'are' , 'none']
1413 ],
1414 'can use new line as delimiter' => [
1415 LF,
1416 "Hello\nall\ntogether",
1417 true,
1418 0,
1419 ['Hello', 'all', 'together']
1420 ],
1421 'works with whitespace separator' => [
1422 "\t",
1423 " a b \t c \t \t d \t e \t u j \t s",
1424 false,
1425 0,
1426 ['a b', 'c', '', 'd', 'e', 'u j', 's']
1427 ],
1428 'works with whitespace separator and limit' => [
1429 "\t",
1430 " a b \t c \t \t d \t e \t u j \t s",
1431 false,
1432 4,
1433 ['a b', 'c', '', "d \t e \t u j \t s"]
1434 ],
1435 'works with whitespace separator and remove empty' => [
1436 "\t",
1437 " a b \t c \t \t d \t e \t u j \t s",
1438 true,
1439 0,
1440 ['a b', 'c', 'd', 'e', 'u j', 's']
1441 ],
1442 'works with whitespace separator remove empty and limit' => [
1443 "\t",
1444 " a b \t c \t \t d \t e \t u j \t s",
1445 true,
1446 3,
1447 ['a b', 'c', "d \t e \t u j \t s"]
1448 ],
1449 ];
1450 }
1451
1452 //////////////////////////////////
1453 // Tests concerning getBytesFromSizeMeasurement
1454 //////////////////////////////////
1455 /**
1456 * Data provider for getBytesFromSizeMeasurement
1457 *
1458 * @return array expected value, input string
1459 */
1460 public function getBytesFromSizeMeasurementDataProvider()
1461 {
1462 return [
1463 '100 kilo Bytes' => ['102400', '100k'],
1464 '100 mega Bytes' => ['104857600', '100m'],
1465 '100 giga Bytes' => ['107374182400', '100g']
1466 ];
1467 }
1468
1469 /**
1470 * @test
1471 * @dataProvider getBytesFromSizeMeasurementDataProvider
1472 */
1473 public function getBytesFromSizeMeasurementCalculatesCorrectByteValue($expected, $byteString)
1474 {
1475 $this->assertEquals($expected, GeneralUtility::getBytesFromSizeMeasurement($byteString));
1476 }
1477
1478 //////////////////////////////////
1479 // Tests concerning getIndpEnv
1480 //////////////////////////////////
1481 /**
1482 * @test
1483 */
1484 public function getIndpEnvTypo3SitePathReturnNonEmptyString()
1485 {
1486 $this->assertTrue(strlen(GeneralUtility::getIndpEnv('TYPO3_SITE_PATH')) >= 1);
1487 }
1488
1489 /**
1490 * @test
1491 */
1492 public function getIndpEnvTypo3SitePathReturnsStringStartingWithSlash()
1493 {
1494 $_SERVER['SCRIPT_NAME'] = '/typo3/';
1495 $result = GeneralUtility::getIndpEnv('TYPO3_SITE_PATH');
1496 $this->assertEquals('/', $result[0]);
1497 }
1498
1499 /**
1500 * @test
1501 */
1502 public function getIndpEnvTypo3SitePathReturnsStringStartingWithDrive()
1503 {
1504 if (TYPO3_OS !== 'WIN') {
1505 $this->markTestSkipped('Test available only on Windows OS.');
1506 }
1507 $result = GeneralUtility::getIndpEnv('TYPO3_SITE_PATH');
1508 $this->assertRegExp('/^[a-z]:\//i', $result);
1509 }
1510
1511 /**
1512 * @test
1513 */
1514 public function getIndpEnvTypo3SitePathReturnsStringEndingWithSlash()
1515 {
1516 $result = GeneralUtility::getIndpEnv('TYPO3_SITE_PATH');
1517 $this->assertEquals('/', $result[strlen($result) - 1]);
1518 }
1519
1520 /**
1521 * @return array
1522 */
1523 public static function hostnameAndPortDataProvider()
1524 {
1525 return [
1526 'localhost ipv4 without port' => ['127.0.0.1', '127.0.0.1', ''],
1527 'localhost ipv4 with port' => ['127.0.0.1:81', '127.0.0.1', '81'],
1528 'localhost ipv6 without port' => ['[::1]', '[::1]', ''],
1529 'localhost ipv6 with port' => ['[::1]:81', '[::1]', '81'],
1530 'ipv6 without port' => ['[2001:DB8::1]', '[2001:DB8::1]', ''],
1531 'ipv6 with port' => ['[2001:DB8::1]:81', '[2001:DB8::1]', '81'],
1532 'hostname without port' => ['lolli.did.this', 'lolli.did.this', ''],
1533 'hostname with port' => ['lolli.did.this:42', 'lolli.did.this', '42']
1534 ];
1535 }
1536
1537 /**
1538 * @test
1539 * @dataProvider hostnameAndPortDataProvider
1540 */
1541 public function getIndpEnvTypo3HostOnlyParsesHostnamesAndIpAdresses($httpHost, $expectedIp)
1542 {
1543 GeneralUtility::flushInternalRuntimeCaches();
1544 $_SERVER['HTTP_HOST'] = $httpHost;
1545 $this->assertEquals($expectedIp, GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY'));
1546 }
1547
1548 /**
1549 * @test
1550 */
1551 public function isAllowedHostHeaderValueReturnsFalseIfTrusedHostsIsNotConfigured()
1552 {
1553 unset($GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern']);
1554 $this->assertFalse(GeneralUtilityFixture::isAllowedHostHeaderValue('evil.foo.bar'));
1555 }
1556
1557 /**
1558 * @return array
1559 */
1560 public static function hostnamesMatchingTrustedHostsConfigurationDataProvider()
1561 {
1562 return [
1563 'hostname without port matching' => ['lolli.did.this', '.*\.did\.this'],
1564 'other hostname without port matching' => ['helmut.did.this', '.*\.did\.this'],
1565 'two different hostnames without port matching 1st host' => ['helmut.is.secure', '(helmut\.is\.secure|lolli\.is\.secure)'],
1566 'two different hostnames without port matching 2nd host' => ['lolli.is.secure', '(helmut\.is\.secure|lolli\.is\.secure)'],
1567 'hostname with port matching' => ['lolli.did.this:42', '.*\.did\.this:42'],
1568 'hostnames are case insensitive 1' => ['lolli.DID.this:42', '.*\.did.this:42'],
1569 'hostnames are case insensitive 2' => ['lolli.did.this:42', '.*\.DID.this:42'],
1570 ];
1571 }
1572
1573 /**
1574 * @return array
1575 */
1576 public static function hostnamesNotMatchingTrustedHostsConfigurationDataProvider()
1577 {
1578 return [
1579 'hostname without port' => ['lolli.did.this', 'helmut\.did\.this'],
1580 'hostname with port, but port not allowed' => ['lolli.did.this:42', 'helmut\.did\.this'],
1581 'two different hostnames in pattern but host header starts with different value #1' => ['sub.helmut.is.secure', '(helmut\.is\.secure|lolli\.is\.secure)'],
1582 'two different hostnames in pattern but host header starts with different value #2' => ['sub.lolli.is.secure', '(helmut\.is\.secure|lolli\.is\.secure)'],
1583 'two different hostnames in pattern but host header ends with different value #1' => ['helmut.is.secure.tld', '(helmut\.is\.secure|lolli\.is\.secure)'],
1584 'two different hostnames in pattern but host header ends with different value #2' => ['lolli.is.secure.tld', '(helmut\.is\.secure|lolli\.is\.secure)'],
1585 ];
1586 }
1587
1588 /**
1589 * @param string $httpHost HTTP_HOST string
1590 * @param string $hostNamePattern trusted hosts pattern
1591 * @test
1592 * @dataProvider hostnamesMatchingTrustedHostsConfigurationDataProvider
1593 */
1594 public function isAllowedHostHeaderValueReturnsTrueIfHostValueMatches($httpHost, $hostNamePattern)
1595 {
1596 $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = $hostNamePattern;
1597 $this->assertTrue(GeneralUtilityFixture::isAllowedHostHeaderValue($httpHost));
1598 }
1599
1600 /**
1601 * @param string $httpHost HTTP_HOST string
1602 * @param string $hostNamePattern trusted hosts pattern
1603 * @test
1604 * @dataProvider hostnamesNotMatchingTrustedHostsConfigurationDataProvider
1605 */
1606 public function isAllowedHostHeaderValueReturnsFalseIfHostValueMatches($httpHost, $hostNamePattern)
1607 {
1608 $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = $hostNamePattern;
1609 $this->assertFalse(GeneralUtilityFixture::isAllowedHostHeaderValue($httpHost));
1610 }
1611
1612 public function serverNamePatternDataProvider()
1613 {
1614 return [
1615 'host value matches server name and server port is default http' => [
1616 'httpHost' => 'secure.web.server',
1617 'serverName' => 'secure.web.server',
1618 'isAllowed' => true,
1619 'serverPort' => '80',
1620 'ssl' => 'Off',
1621 ],
1622 'host value matches server name if compared case insensitive 1' => [
1623 'httpHost' => 'secure.web.server',
1624 'serverName' => 'secure.WEB.server',
1625 'isAllowed' => true,
1626 ],
1627 'host value matches server name if compared case insensitive 2' => [
1628 'httpHost' => 'secure.WEB.server',
1629 'serverName' => 'secure.web.server',
1630 'isAllowed' => true,
1631 ],
1632 'host value matches server name and server port is default https' => [
1633 'httpHost' => 'secure.web.server',
1634 'serverName' => 'secure.web.server',
1635 'isAllowed' => true,
1636 'serverPort' => '443',
1637 'ssl' => 'On',
1638 ],
1639 'host value matches server name and server port' => [
1640 'httpHost' => 'secure.web.server:88',
1641 'serverName' => 'secure.web.server',
1642 'isAllowed' => true,
1643 'serverPort' => '88',
1644 ],
1645 'host value matches server name case insensitive 1 and server port' => [
1646 'httpHost' => 'secure.WEB.server:88',
1647 'serverName' => 'secure.web.server',
1648 'isAllowed' => true,
1649 'serverPort' => '88',
1650 ],
1651 'host value matches server name case insensitive 2 and server port' => [
1652 'httpHost' => 'secure.web.server:88',
1653 'serverName' => 'secure.WEB.server',
1654 'isAllowed' => true,
1655 'serverPort' => '88',
1656 ],
1657 'host value is ipv6 but matches server name and server port' => [
1658 'httpHost' => '[::1]:81',
1659 'serverName' => '[::1]',
1660 'isAllowed' => true,
1661 'serverPort' => '81',
1662 ],
1663 'host value does not match server name' => [
1664 'httpHost' => 'insecure.web.server',
1665 'serverName' => 'secure.web.server',
1666 'isAllowed' => false,
1667 ],
1668 'host value does not match server port' => [
1669 'httpHost' => 'secure.web.server:88',
1670 'serverName' => 'secure.web.server',
1671 'isAllowed' => false,
1672 'serverPort' => '89',
1673 ],
1674 'host value has default port that does not match server port' => [
1675 'httpHost' => 'secure.web.server',
1676 'serverName' => 'secure.web.server',
1677 'isAllowed' => false,
1678 'serverPort' => '81',
1679 'ssl' => 'Off',
1680 ],
1681 'host value has default port that does not match server ssl port' => [
1682 'httpHost' => 'secure.web.server',
1683 'serverName' => 'secure.web.server',
1684 'isAllowed' => false,
1685 'serverPort' => '444',
1686 'ssl' => 'On',
1687 ],
1688 ];
1689 }
1690
1691 /**
1692 * @param string $httpHost
1693 * @param string $serverName
1694 * @param bool $isAllowed
1695 * @param string $serverPort
1696 * @param string $ssl
1697 *
1698 * @test
1699 * @dataProvider serverNamePatternDataProvider
1700 */
1701 public function isAllowedHostHeaderValueWorksCorrectlyWithWithServerNamePattern($httpHost, $serverName, $isAllowed, $serverPort = '80', $ssl = 'Off')
1702 {
1703 $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = GeneralUtility::ENV_TRUSTED_HOSTS_PATTERN_SERVER_NAME;
1704 $_SERVER['SERVER_NAME'] = $serverName;
1705 $_SERVER['SERVER_PORT'] = $serverPort;
1706 $_SERVER['HTTPS'] = $ssl;
1707 $this->assertSame($isAllowed, GeneralUtilityFixture::isAllowedHostHeaderValue($httpHost));
1708 }
1709
1710 /**
1711 * @test
1712 */
1713 public function allGetIndpEnvCallsRelatedToHostNamesCallIsAllowedHostHeaderValue()
1714 {
1715 GeneralUtilityFixture::getIndpEnv('HTTP_HOST');
1716 GeneralUtility::flushInternalRuntimeCaches();
1717 GeneralUtilityFixture::getIndpEnv('TYPO3_HOST_ONLY');
1718 GeneralUtility::flushInternalRuntimeCaches();
1719 GeneralUtilityFixture::getIndpEnv('TYPO3_REQUEST_HOST');
1720 GeneralUtility::flushInternalRuntimeCaches();
1721 GeneralUtilityFixture::getIndpEnv('TYPO3_REQUEST_URL');
1722 $this->assertSame(4, GeneralUtilityFixture::$isAllowedHostHeaderValueCallCount);
1723 }
1724
1725 /**
1726 * @param string $httpHost HTTP_HOST string
1727 * @param string $hostNamePattern trusted hosts pattern
1728 * @test
1729 * @dataProvider hostnamesNotMatchingTrustedHostsConfigurationDataProvider
1730 */
1731 public function getIndpEnvForHostThrowsExceptionForNotAllowedHostnameValues($httpHost, $hostNamePattern)
1732 {
1733 $this->expectException(\UnexpectedValueException::class);
1734 $this->expectExceptionCode(1396795884);
1735
1736 $_SERVER['HTTP_HOST'] = $httpHost;
1737 $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = $hostNamePattern;
1738 GeneralUtilityFixture::getIndpEnv('HTTP_HOST');
1739 }
1740
1741 /**
1742 * @param string $httpHost HTTP_HOST string
1743 * @param string $hostNamePattern trusted hosts pattern (not used in this test currently)
1744 * @test
1745 * @dataProvider hostnamesNotMatchingTrustedHostsConfigurationDataProvider
1746 */
1747 public function getIndpEnvForHostAllowsAllHostnameValuesIfHostPatternIsSetToAllowAll($httpHost, $hostNamePattern)
1748 {
1749 $_SERVER['HTTP_HOST'] = $httpHost;
1750 $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = GeneralUtility::ENV_TRUSTED_HOSTS_PATTERN_ALLOW_ALL;
1751 $this->assertSame($httpHost, GeneralUtility::getIndpEnv('HTTP_HOST'));
1752 }
1753
1754 /**
1755 * @test
1756 * @dataProvider hostnameAndPortDataProvider
1757 */
1758 public function getIndpEnvTypo3PortParsesHostnamesAndIpAdresses($httpHost, $dummy, $expectedPort)
1759 {
1760 $_SERVER['HTTP_HOST'] = $httpHost;
1761 $this->assertEquals($expectedPort, GeneralUtility::getIndpEnv('TYPO3_PORT'));
1762 }
1763
1764 //////////////////////////////////
1765 // Tests concerning underscoredToUpperCamelCase
1766 //////////////////////////////////
1767 /**
1768 * Data provider for underscoredToUpperCamelCase
1769 *
1770 * @return array expected, input string
1771 */
1772 public function underscoredToUpperCamelCaseDataProvider()
1773 {
1774 return [
1775 'single word' => ['Blogexample', 'blogexample'],
1776 'multiple words' => ['BlogExample', 'blog_example']
1777 ];
1778 }
1779
1780 /**
1781 * @test
1782 * @dataProvider underscoredToUpperCamelCaseDataProvider
1783 */
1784 public function underscoredToUpperCamelCase($expected, $inputString)
1785 {
1786 $this->assertEquals($expected, GeneralUtility::underscoredToUpperCamelCase($inputString));
1787 }
1788
1789 //////////////////////////////////
1790 // Tests concerning underscoredToLowerCamelCase
1791 //////////////////////////////////
1792 /**
1793 * Data provider for underscoredToLowerCamelCase
1794 *
1795 * @return array expected, input string
1796 */
1797 public function underscoredToLowerCamelCaseDataProvider()
1798 {
1799 return [
1800 'single word' => ['minimalvalue', 'minimalvalue'],
1801 'multiple words' => ['minimalValue', 'minimal_value']
1802 ];
1803 }
1804
1805 /**
1806 * @test
1807 * @dataProvider underscoredToLowerCamelCaseDataProvider
1808 */
1809 public function underscoredToLowerCamelCase($expected, $inputString)
1810 {
1811 $this->assertEquals($expected, GeneralUtility::underscoredToLowerCamelCase($inputString));
1812 }
1813
1814 //////////////////////////////////
1815 // Tests concerning camelCaseToLowerCaseUnderscored
1816 //////////////////////////////////
1817 /**
1818 * Data provider for camelCaseToLowerCaseUnderscored
1819 *
1820 * @return array expected, input string
1821 */
1822 public function camelCaseToLowerCaseUnderscoredDataProvider()
1823 {
1824 return [
1825 'single word' => ['blogexample', 'blogexample'],
1826 'single word starting upper case' => ['blogexample', 'Blogexample'],
1827 'two words starting lower case' => ['minimal_value', 'minimalValue'],
1828 'two words starting upper case' => ['blog_example', 'BlogExample']
1829 ];
1830 }
1831
1832 /**
1833 * @test
1834 * @dataProvider camelCaseToLowerCaseUnderscoredDataProvider
1835 */
1836 public function camelCaseToLowerCaseUnderscored($expected, $inputString)
1837 {
1838 $this->assertEquals($expected, GeneralUtility::camelCaseToLowerCaseUnderscored($inputString));
1839 }
1840
1841 //////////////////////////////////
1842 // Tests concerning isValidUrl
1843 //////////////////////////////////
1844 /**
1845 * Data provider for valid isValidUrl's
1846 *
1847 * @return array Valid resource
1848 */
1849 public function validUrlValidResourceDataProvider()
1850 {
1851 return [
1852 'http' => ['http://www.example.org/'],
1853 'http without trailing slash' => ['http://qwe'],
1854 'http directory with trailing slash' => ['http://www.example/img/dir/'],
1855 'http directory without trailing slash' => ['http://www.example/img/dir'],
1856 'http index.html' => ['http://example.com/index.html'],
1857 'http index.php' => ['http://www.example.com/index.php'],
1858 'http test.png' => ['http://www.example/img/test.png'],
1859 'http username password querystring and ancher' => ['https://user:pw@www.example.org:80/path?arg=value#fragment'],
1860 'file' => ['file:///tmp/test.c'],
1861 'file directory' => ['file://foo/bar'],
1862 'ftp directory' => ['ftp://ftp.example.com/tmp/'],
1863 'mailto' => ['mailto:foo@bar.com'],
1864 'news' => ['news:news.php.net'],
1865 'telnet' => ['telnet://192.0.2.16:80/'],
1866 'ldap' => ['ldap://[2001:db8::7]/c=GB?objectClass?one'],
1867 'http punycode domain name' => ['http://www.xn--bb-eka.at'],
1868 'http punicode subdomain' => ['http://xn--h-zfa.oebb.at'],
1869 'http domain-name umlauts' => ['http://www.öbb.at'],
1870 'http subdomain umlauts' => ['http://äh.oebb.at'],
1871 ];
1872 }
1873
1874 /**
1875 * @test
1876 * @dataProvider validUrlValidResourceDataProvider
1877 */
1878 public function validURLReturnsTrueForValidResource($url)
1879 {
1880 $this->assertTrue(GeneralUtility::isValidUrl($url));
1881 }
1882
1883 /**
1884 * Data provider for invalid isValidUrl's
1885 *
1886 * @return array Invalid ressource
1887 */
1888 public function isValidUrlInvalidRessourceDataProvider()
1889 {
1890 return [
1891 'http missing colon' => ['http//www.example/wrong/url/'],
1892 'http missing slash' => ['http:/www.example'],
1893 'hostname only' => ['www.example.org/'],
1894 'file missing protocol specification' => ['/tmp/test.c'],
1895 'slash only' => ['/'],
1896 'string http://' => ['http://'],
1897 'string http:/' => ['http:/'],
1898 'string http:' => ['http:'],
1899 'string http' => ['http'],
1900 'empty string' => [''],
1901 'string -1' => ['-1'],
1902 'string array()' => ['array()'],
1903 'random string' => ['qwe'],
1904 'http directory umlauts' => ['http://www.oebb.at/äöü/'],
1905 ];
1906 }
1907
1908 /**
1909 * @test
1910 * @dataProvider isValidUrlInvalidRessourceDataProvider
1911 */
1912 public function validURLReturnsFalseForInvalidRessoure($url)
1913 {
1914 $this->assertFalse(GeneralUtility::isValidUrl($url));
1915 }
1916
1917 //////////////////////////////////
1918 // Tests concerning isOnCurrentHost
1919 //////////////////////////////////
1920 /**
1921 * @test
1922 */
1923 public function isOnCurrentHostReturnsTrueWithCurrentHost()
1924 {
1925 $testUrl = GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL');
1926 $this->assertTrue(GeneralUtility::isOnCurrentHost($testUrl));
1927 }
1928
1929 /**
1930 * Data provider for invalid isOnCurrentHost's
1931 *
1932 * @return array Invalid Hosts
1933 */
1934 public function checkisOnCurrentHostInvalidHosts()
1935 {
1936 return [
1937 'empty string' => [''],
1938 'arbitrary string' => ['arbitrary string'],
1939 'localhost IP' => ['127.0.0.1'],
1940 'relative path' => ['./relpath/file.txt'],
1941 'absolute path' => ['/abspath/file.txt?arg=value'],
1942 'differnt host' => [GeneralUtility::getIndpEnv('TYPO3_REQUEST_HOST') . '.example.org']
1943 ];
1944 }
1945
1946 ////////////////////////////////////////
1947 // Tests concerning sanitizeLocalUrl
1948 ////////////////////////////////////////
1949 /**
1950 * Data provider for valid sanitizeLocalUrl paths
1951 *
1952 * @return array Valid url
1953 */
1954 public function sanitizeLocalUrlValidPathsDataProvider()
1955 {
1956 return [
1957 'alt_intro.php' => ['alt_intro.php'],
1958 'alt_intro.php?foo=1&bar=2' => ['alt_intro.php?foo=1&bar=2'],
1959 '../index.php' => ['../index.php'],
1960 '../typo3/alt_intro.php' => ['../typo3/alt_intro.php'],
1961 '../~userDirectory/index.php' => ['../~userDirectory/index.php'],
1962 '../typo3/index.php?var1=test-case&var2=~user' => ['../typo3/index.php?var1=test-case&var2=~user'],
1963 PATH_site . 'typo3/alt_intro.php' => [PATH_site . 'typo3/alt_intro.php'],
1964 ];
1965 }
1966
1967 /**
1968 * @test
1969 * @param string $path
1970 * @dataProvider sanitizeLocalUrlValidPathsDataProvider
1971 */
1972 public function sanitizeLocalUrlAcceptsNotEncodedValidPaths($path)
1973 {
1974 $this->assertEquals($path, GeneralUtility::sanitizeLocalUrl($path));
1975 }
1976
1977 /**
1978 * @test
1979 * @param string $path
1980 * @dataProvider sanitizeLocalUrlValidPathsDataProvider
1981 */
1982 public function sanitizeLocalUrlAcceptsEncodedValidPaths($path)
1983 {
1984 $this->assertEquals(rawurlencode($path), GeneralUtility::sanitizeLocalUrl(rawurlencode($path)));
1985 }
1986
1987 /**
1988 * Data provider for valid sanitizeLocalUrl's
1989 *
1990 * @return array Valid url
1991 */
1992 public function sanitizeLocalUrlValidUrlsDataProvider()
1993 {
1994 $host = 'localhost';
1995 $subDirectory = '/cms/';
1996
1997 return [
1998 $subDirectory . 'typo3/alt_intro.php' => [
1999 $subDirectory . 'typo3/alt_intro.php',
2000 $host,
2001 $subDirectory,
2002 ],
2003 $subDirectory . 'index.php' => [
2004 $subDirectory . 'index.php',
2005 $host,
2006 $subDirectory,
2007 ],
2008 'http://' . $host . '/typo3/alt_intro.php' => [
2009 'http://' . $host . '/typo3/alt_intro.php',
2010 $host,
2011 '',
2012 ],
2013 'http://' . $host . $subDirectory . 'typo3/alt_intro.php' => [
2014 'http://' . $host . $subDirectory . 'typo3/alt_intro.php',
2015 $host,
2016 $subDirectory,
2017 ],
2018 ];
2019 }
2020
2021 /**
2022 * @test
2023 * @param string $url
2024 * @param string $host
2025 * @param string $subDirectory
2026 * @dataProvider sanitizeLocalUrlValidUrlsDataProvider
2027 */
2028 public function sanitizeLocalUrlAcceptsNotEncodedValidUrls($url, $host, $subDirectory)
2029 {
2030 $_SERVER['HTTP_HOST'] = $host;
2031 $_SERVER['SCRIPT_NAME'] = $subDirectory . 'typo3/index.php';
2032 GeneralUtility::flushInternalRuntimeCaches();
2033 $this->assertEquals($url, GeneralUtility::sanitizeLocalUrl($url));
2034 }
2035
2036 /**
2037 * @test
2038 * @param string $url
2039 * @param string $host
2040 * @param string $subDirectory
2041 * @dataProvider sanitizeLocalUrlValidUrlsDataProvider
2042 */
2043 public function sanitizeLocalUrlAcceptsEncodedValidUrls($url, $host, $subDirectory)
2044 {
2045 $_SERVER['HTTP_HOST'] = $host;
2046 $_SERVER['SCRIPT_NAME'] = $subDirectory . 'typo3/index.php';
2047 GeneralUtility::flushInternalRuntimeCaches();
2048 $this->assertEquals(rawurlencode($url), GeneralUtility::sanitizeLocalUrl(rawurlencode($url)));
2049 }
2050
2051 /**
2052 * Data provider for invalid sanitizeLocalUrl's
2053 *
2054 * @return array Valid url
2055 */
2056 public function sanitizeLocalUrlInvalidDataProvider()
2057 {
2058 return [
2059 'empty string' => [''],
2060 'http domain' => ['http://www.google.de/'],
2061 'https domain' => ['https://www.google.de/'],
2062 'XSS attempt' => ['" onmouseover="alert(123)"'],
2063 'invalid URL, UNC path' => ['\\\\foo\\bar\\'],
2064 'invalid URL, HTML break out attempt' => ['" >blabuubb'],
2065 'base64 encoded string' => ['data:%20text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4='],
2066 ];
2067 }
2068
2069 /**
2070 * @test
2071 * @dataProvider sanitizeLocalUrlInvalidDataProvider
2072 */
2073 public function sanitizeLocalUrlDeniesPlainInvalidUrls($url)
2074 {
2075 $this->assertEquals('', GeneralUtility::sanitizeLocalUrl($url));
2076 }
2077
2078 /**
2079 * @test
2080 * @dataProvider sanitizeLocalUrlInvalidDataProvider
2081 */
2082 public function sanitizeLocalUrlDeniesEncodedInvalidUrls($url)
2083 {
2084 $this->assertEquals('', GeneralUtility::sanitizeLocalUrl(rawurlencode($url)));
2085 }
2086
2087 ////////////////////////////////////////
2088 // Tests concerning unlink_tempfile
2089 ////////////////////////////////////////
2090
2091 /**
2092 * @test
2093 */
2094 public function unlink_tempfileRemovesValidFileInTypo3temp()
2095 {
2096 $fixtureFile = __DIR__ . '/Fixtures/clear.gif';
2097 $testFilename = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_') . '.gif';
2098 @copy($fixtureFile, $testFilename);
2099 GeneralUtility::unlink_tempfile($testFilename);
2100 $fileExists = file_exists($testFilename);
2101 $this->assertFalse($fileExists);
2102 }
2103
2104 /**
2105 * @test
2106 */
2107 public function unlink_tempfileRemovesHiddenFile()
2108 {
2109 $fixtureFile = __DIR__ . '/Fixtures/clear.gif';
2110 $testFilename = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('.test_') . '.gif';
2111 @copy($fixtureFile, $testFilename);
2112 GeneralUtility::unlink_tempfile($testFilename);
2113 $fileExists = file_exists($testFilename);
2114 $this->assertFalse($fileExists);
2115 }
2116
2117 /**
2118 * @test
2119 */
2120 public function unlink_tempfileReturnsTrueIfFileWasRemoved()
2121 {
2122 $fixtureFile = __DIR__ . '/Fixtures/clear.gif';
2123 $testFilename = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_') . '.gif';
2124 @copy($fixtureFile, $testFilename);
2125 $returnValue = GeneralUtility::unlink_tempfile($testFilename);
2126 $this->assertTrue($returnValue);
2127 }
2128
2129 /**
2130 * @test
2131 */
2132 public function unlink_tempfileReturnsNullIfFileDoesNotExist()
2133 {
2134 $returnValue = GeneralUtility::unlink_tempfile(PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('i_do_not_exist'));
2135 $this->assertNull($returnValue);
2136 }
2137
2138 /**
2139 * @test
2140 */
2141 public function unlink_tempfileReturnsNullIfFileIsNowWithinTypo3temp()
2142 {
2143 $returnValue = GeneralUtility::unlink_tempfile('/tmp/typo3-unit-test-unlink_tempfile');
2144 $this->assertNull($returnValue);
2145 }
2146
2147 //////////////////////////////////////
2148 // Tests concerning tempnam
2149 //////////////////////////////////////
2150
2151 /**
2152 * @test
2153 */
2154 public function tempnamReturnsPathStartingWithGivenPrefix()
2155 {
2156 $filePath = GeneralUtility::tempnam('foo');
2157 $this->testFilesToDelete[] = $filePath;
2158 $fileName = basename($filePath);
2159 $this->assertStringStartsWith('foo', $fileName);
2160 }
2161
2162 /**
2163 * @test
2164 */
2165 public function tempnamReturnsPathWithoutBackslashes()
2166 {
2167 $filePath = GeneralUtility::tempnam('foo');
2168 $this->testFilesToDelete[] = $filePath;
2169 $this->assertNotContains('\\', $filePath);
2170 }
2171
2172 /**
2173 * @test
2174 */
2175 public function tempnamReturnsAbsolutePathInsideDocumentRoot()
2176 {
2177 $filePath = GeneralUtility::tempnam('foo');
2178 $this->testFilesToDelete[] = $filePath;
2179 $this->assertStringStartsWith(PATH_site, $filePath);
2180 }
2181
2182 //////////////////////////////////////
2183 // Tests concerning removeDotsFromTS
2184 //////////////////////////////////////
2185 /**
2186 * @test
2187 */
2188 public function removeDotsFromTypoScriptSucceedsWithDottedArray()
2189 {
2190 $typoScript = [
2191 'propertyA.' => [
2192 'keyA.' => [
2193 'valueA' => 1
2194 ],
2195 'keyB' => 2
2196 ],
2197 'propertyB' => 3
2198 ];
2199 $expectedResult = [
2200 'propertyA' => [
2201 'keyA' => [
2202 'valueA' => 1
2203 ],
2204 'keyB' => 2
2205 ],
2206 'propertyB' => 3
2207 ];
2208 $this->assertEquals($expectedResult, GeneralUtility::removeDotsFromTS($typoScript));
2209 }
2210
2211 /**
2212 * @test
2213 */
2214 public function removeDotsFromTypoScriptOverridesSubArray()
2215 {
2216 $typoScript = [
2217 'propertyA.' => [
2218 'keyA' => 'getsOverridden',
2219 'keyA.' => [
2220 'valueA' => 1
2221 ],
2222 'keyB' => 2
2223 ],
2224 'propertyB' => 3
2225 ];
2226 $expectedResult = [
2227 'propertyA' => [
2228 'keyA' => [
2229 'valueA' => 1
2230 ],
2231 'keyB' => 2
2232 ],
2233 'propertyB' => 3
2234 ];
2235 $this->assertEquals($expectedResult, GeneralUtility::removeDotsFromTS($typoScript));
2236 }
2237
2238 /**
2239 * @test
2240 */
2241 public function removeDotsFromTypoScriptOverridesWithScalar()
2242 {
2243 $typoScript = [
2244 'propertyA.' => [
2245 'keyA.' => [
2246 'valueA' => 1
2247 ],
2248 'keyA' => 'willOverride',
2249 'keyB' => 2
2250 ],
2251 'propertyB' => 3
2252 ];
2253 $expectedResult = [
2254 'propertyA' => [
2255 'keyA' => 'willOverride',
2256 'keyB' => 2
2257 ],
2258 'propertyB' => 3
2259 ];
2260 $this->assertEquals($expectedResult, GeneralUtility::removeDotsFromTS($typoScript));
2261 }
2262
2263 //////////////////////////////////////
2264 // Tests concerning get_dirs
2265 //////////////////////////////////////
2266 /**
2267 * @test
2268 */
2269 public function getDirsReturnsArrayOfDirectoriesFromGivenDirectory()
2270 {
2271 $path = PATH_typo3conf;
2272 $directories = GeneralUtility::get_dirs($path);
2273 $this->assertInternalType(\PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $directories);
2274 }
2275
2276 /**
2277 * @test
2278 */
2279 public function getDirsReturnsStringErrorOnPathFailure()
2280 {
2281 $path = 'foo';
2282 $result = GeneralUtility::get_dirs($path);
2283 $expectedResult = 'error';
2284 $this->assertEquals($expectedResult, $result);
2285 }
2286
2287 //////////////////////////////////
2288 // Tests concerning hmac
2289 //////////////////////////////////
2290 /**
2291 * @test
2292 */
2293 public function hmacReturnsHashOfProperLength()
2294 {
2295 $hmac = GeneralUtility::hmac('message');
2296 $this->assertTrue(!empty($hmac) && is_string($hmac));
2297 $this->assertTrue(strlen($hmac) == 40);
2298 }
2299
2300 /**
2301 * @test
2302 */
2303 public function hmacReturnsEqualHashesForEqualInput()
2304 {
2305 $msg0 = 'message';
2306 $msg1 = 'message';
2307 $this->assertEquals(GeneralUtility::hmac($msg0), GeneralUtility::hmac($msg1));
2308 }
2309
2310 /**
2311 * @test
2312 */
2313 public function hmacReturnsNoEqualHashesForNonEqualInput()
2314 {
2315 $msg0 = 'message0';
2316 $msg1 = 'message1';
2317 $this->assertNotEquals(GeneralUtility::hmac($msg0), GeneralUtility::hmac($msg1));
2318 }
2319
2320 //////////////////////////////////
2321 // Tests concerning quoteJSvalue
2322 //////////////////////////////////
2323 /**
2324 * Data provider for quoteJSvalueTest.
2325 *
2326 * @return array
2327 */
2328 public function quoteJsValueDataProvider()
2329 {
2330 return [
2331 'Immune characters are returned as is' => [
2332 '._,',
2333 '._,'
2334 ],
2335 'Alphanumerical characters are returned as is' => [
2336 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
2337 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
2338 ],
2339 'Angle brackets and ampersand are encoded' => [
2340 '<>&',
2341 '\\u003C\\u003E\\u0026'
2342 ],
2343 'Quotes and backslashes are encoded' => [
2344 '"\'\\',
2345 '\\u0022\\u0027\\u005C'
2346 ],
2347 'Forward slashes are escaped' => [
2348 '</script>',
2349 '\\u003C\\/script\\u003E'
2350 ],
2351 'Empty string stays empty' => [
2352 '',
2353 ''
2354 ],
2355 'Exclamation mark and space are properly encoded' => [
2356 'Hello World!',
2357 'Hello\\u0020World\\u0021'
2358 ],
2359 'Whitespaces are properly encoded' => [
2360 TAB . LF . CR . ' ',
2361 '\\u0009\\u000A\\u000D\\u0020'
2362 ],
2363 'Null byte is properly encoded' => [
2364 chr(0),
2365 '\\u0000'
2366 ],
2367 'Umlauts are properly encoded' => [
2368 'ÜüÖöÄä',
2369 '\\u00dc\\u00fc\\u00d6\\u00f6\\u00c4\\u00e4'
2370 ]
2371 ];
2372 }
2373
2374 /**
2375 * @test
2376 * @param string $input
2377 * @param string $expected
2378 * @dataProvider quoteJsValueDataProvider
2379 */
2380 public function quoteJsValueTest($input, $expected)
2381 {
2382 $this->assertSame('\'' . $expected . '\'', GeneralUtility::quoteJSvalue($input));
2383 }
2384
2385 ///////////////////////////////
2386 // Tests concerning _GETset()
2387 ///////////////////////////////
2388 /**
2389 * @test
2390 */
2391 public function getSetWritesArrayToGetSystemVariable()
2392 {
2393 $_GET = [];
2394 $GLOBALS['HTTP_GET_VARS'] = [];
2395 $getParameters = ['foo' => 'bar'];
2396 GeneralUtility::_GETset($getParameters);
2397 $this->assertSame($getParameters, $_GET);
2398 }
2399
2400 /**
2401 * @test
2402 */
2403 public function getSetWritesArrayToGlobalsHttpGetVars()
2404 {
2405 $_GET = [];
2406 $GLOBALS['HTTP_GET_VARS'] = [];
2407 $getParameters = ['foo' => 'bar'];
2408 GeneralUtility::_GETset($getParameters);
2409 $this->assertSame($getParameters, $GLOBALS['HTTP_GET_VARS']);
2410 }
2411
2412 /**
2413 * @test
2414 */
2415 public function getSetForArrayDropsExistingValues()
2416 {
2417 $_GET = [];
2418 $GLOBALS['HTTP_GET_VARS'] = [];
2419 GeneralUtility::_GETset(['foo' => 'bar']);
2420 GeneralUtility::_GETset(['oneKey' => 'oneValue']);
2421 $this->assertEquals(['oneKey' => 'oneValue'], $GLOBALS['HTTP_GET_VARS']);
2422 }
2423
2424 /**
2425 * @test
2426 */
2427 public function getSetAssignsOneValueToOneKey()
2428 {
2429 $_GET = [];
2430 $GLOBALS['HTTP_GET_VARS'] = [];
2431 GeneralUtility::_GETset('oneValue', 'oneKey');
2432 $this->assertEquals('oneValue', $GLOBALS['HTTP_GET_VARS']['oneKey']);
2433 }
2434
2435 /**
2436 * @test
2437 */
2438 public function getSetForOneValueDoesNotDropUnrelatedValues()
2439 {
2440 $_GET = [];
2441 $GLOBALS['HTTP_GET_VARS'] = [];
2442 GeneralUtility::_GETset(['foo' => 'bar']);
2443 GeneralUtility::_GETset('oneValue', 'oneKey');
2444 $this->assertEquals(['foo' => 'bar', 'oneKey' => 'oneValue'], $GLOBALS['HTTP_GET_VARS']);
2445 }
2446
2447 /**
2448 * @test
2449 */
2450 public function getSetCanAssignsAnArrayToASpecificArrayElement()
2451 {
2452 $_GET = [];
2453 $GLOBALS['HTTP_GET_VARS'] = [];
2454 GeneralUtility::_GETset(['childKey' => 'oneValue'], 'parentKey');
2455 $this->assertEquals(['parentKey' => ['childKey' => 'oneValue']], $GLOBALS['HTTP_GET_VARS']);
2456 }
2457
2458 /**
2459 * @test
2460 */
2461 public function getSetCanAssignAStringValueToASpecificArrayChildElement()
2462 {
2463 $_GET = [];
2464 $GLOBALS['HTTP_GET_VARS'] = [];
2465 GeneralUtility::_GETset('oneValue', 'parentKey|childKey');
2466 $this->assertEquals(['parentKey' => ['childKey' => 'oneValue']], $GLOBALS['HTTP_GET_VARS']);
2467 }
2468
2469 /**
2470 * @test
2471 */
2472 public function getSetCanAssignAnArrayToASpecificArrayChildElement()
2473 {
2474 $_GET = [];
2475 $GLOBALS['HTTP_GET_VARS'] = [];
2476 GeneralUtility::_GETset(['key1' => 'value1', 'key2' => 'value2'], 'parentKey|childKey');
2477 $this->assertEquals([
2478 'parentKey' => [
2479 'childKey' => ['key1' => 'value1', 'key2' => 'value2']
2480 ]
2481 ], $GLOBALS['HTTP_GET_VARS']);
2482 }
2483
2484 ///////////////////////////
2485 // Tests concerning minifyJavaScript
2486 ///////////////////////////
2487 /**
2488 * @test
2489 */
2490 public function minifyJavaScriptReturnsInputStringIfNoHookIsRegistered()
2491 {
2492 unset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['minifyJavaScript']);
2493 $testString = $this->getUniqueId('string');
2494 $this->assertSame($testString, GeneralUtility::minifyJavaScript($testString));
2495 }
2496
2497 /**
2498 * Callback function used in
2499 * minifyJavaScriptReturnsErrorStringOfHookException and
2500 * minifyJavaScriptWritesExceptionMessageToDevLog
2501 *
2502 * @throws \RuntimeException
2503 */
2504 public function minifyJavaScriptErroneousCallback()
2505 {
2506 throw new \RuntimeException('foo', 1344888548);
2507 }
2508
2509 ///////////////////////////////
2510 // Tests concerning fixPermissions
2511 ///////////////////////////////
2512 /**
2513 * @test
2514 */
2515 public function fixPermissionsSetsGroup()
2516 {
2517 if (TYPO3_OS === 'WIN') {
2518 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2519 }
2520 if (!function_exists('posix_getegid')) {
2521 $this->markTestSkipped('Function posix_getegid() not available, fixPermissionsSetsGroup() tests skipped');
2522 }
2523 if (posix_getegid() === -1) {
2524 $this->markTestSkipped('The fixPermissionsSetsGroup() is not available on Mac OS because posix_getegid() always returns -1 on Mac OS.');
2525 }
2526 // Create and prepare test file
2527 $filename = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2528 GeneralUtilityFilesystemFixture::writeFileToTypo3tempDir($filename, '42');
2529 $currentGroupId = posix_getegid();
2530 // Set target group and run method
2531 $GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup'] = $currentGroupId;
2532 GeneralUtilityFilesystemFixture::fixPermissions($filename);
2533 clearstatcache();
2534 $this->assertEquals($currentGroupId, filegroup($filename));
2535 }
2536
2537 /**
2538 * @test
2539 */
2540 public function fixPermissionsSetsPermissionsToFile()
2541 {
2542 if (TYPO3_OS === 'WIN') {
2543 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2544 }
2545 // Create and prepare test file
2546 $filename = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2547 GeneralUtilityFilesystemFixture::writeFileToTypo3tempDir($filename, '42');
2548 chmod($filename, 482);
2549 // Set target permissions and run method
2550 $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
2551 $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($filename);
2552 clearstatcache();
2553 $this->assertTrue($fixPermissionsResult);
2554 $this->assertEquals('0660', substr(decoct(fileperms($filename)), 2));
2555 }
2556
2557 /**
2558 * @test
2559 */
2560 public function fixPermissionsSetsPermissionsToHiddenFile()
2561 {
2562 if (TYPO3_OS === 'WIN') {
2563 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2564 }
2565 // Create and prepare test file
2566 $filename = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2567 GeneralUtilityFilesystemFixture::writeFileToTypo3tempDir($filename, '42');
2568 chmod($filename, 482);
2569 // Set target permissions and run method
2570 $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
2571 $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($filename);
2572 clearstatcache();
2573 $this->assertTrue($fixPermissionsResult);
2574 $this->assertEquals('0660', substr(decoct(fileperms($filename)), 2));
2575 }
2576
2577 /**
2578 * @test
2579 */
2580 public function fixPermissionsSetsPermissionsToDirectory()
2581 {
2582 if (TYPO3_OS === 'WIN') {
2583 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2584 }
2585 // Create and prepare test directory
2586 $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2587 GeneralUtilityFilesystemFixture::mkdir($directory);
2588 chmod($directory, 1551);
2589 // Set target permissions and run method
2590 $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0770';
2591 $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($directory);
2592 clearstatcache();
2593 $this->assertTrue($fixPermissionsResult);
2594 $this->assertEquals('0770', substr(decoct(fileperms($directory)), 1));
2595 }
2596
2597 /**
2598 * @test
2599 */
2600 public function fixPermissionsSetsPermissionsToDirectoryWithTrailingSlash()
2601 {
2602 if (TYPO3_OS === 'WIN') {
2603 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2604 }
2605 // Create and prepare test directory
2606 $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2607 GeneralUtilityFilesystemFixture::mkdir($directory);
2608 chmod($directory, 1551);
2609 // Set target permissions and run method
2610 $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0770';
2611 $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($directory . '/');
2612 // Get actual permissions and clean up
2613 clearstatcache();
2614 $this->assertTrue($fixPermissionsResult);
2615 $this->assertEquals('0770', substr(decoct(fileperms($directory)), 1));
2616 }
2617
2618 /**
2619 * @test
2620 */
2621 public function fixPermissionsSetsPermissionsToHiddenDirectory()
2622 {
2623 if (TYPO3_OS === 'WIN') {
2624 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2625 }
2626 // Create and prepare test directory
2627 $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2628 GeneralUtilityFilesystemFixture::mkdir($directory);
2629 chmod($directory, 1551);
2630 // Set target permissions and run method
2631 $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0770';
2632 $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($directory);
2633 // Get actual permissions and clean up
2634 clearstatcache();
2635 $this->assertTrue($fixPermissionsResult);
2636 $this->assertEquals('0770', substr(decoct(fileperms($directory)), 1));
2637 }
2638
2639 /**
2640 * @test
2641 */
2642 public function fixPermissionsCorrectlySetsPermissionsRecursive()
2643 {
2644 if (TYPO3_OS === 'WIN') {
2645 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2646 }
2647 // Create and prepare test directory and file structure
2648 $baseDirectory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2649 GeneralUtilityFilesystemFixture::mkdir($baseDirectory);
2650 chmod($baseDirectory, 1751);
2651 GeneralUtilityFilesystemFixture::writeFileToTypo3tempDir($baseDirectory . '/file', '42');
2652 chmod($baseDirectory . '/file', 482);
2653 GeneralUtilityFilesystemFixture::mkdir($baseDirectory . '/foo');
2654 chmod($baseDirectory . '/foo', 1751);
2655 GeneralUtilityFilesystemFixture::writeFileToTypo3tempDir($baseDirectory . '/foo/file', '42');
2656 chmod($baseDirectory . '/foo/file', 482);
2657 GeneralUtilityFilesystemFixture::mkdir($baseDirectory . '/.bar');
2658 chmod($baseDirectory . '/.bar', 1751);
2659 // Use this if writeFileToTypo3tempDir is fixed to create hidden files in subdirectories
2660 // \TYPO3\CMS\Core\Utility\GeneralUtility::writeFileToTypo3tempDir($baseDirectory . '/.bar/.file', '42');
2661 // \TYPO3\CMS\Core\Utility\GeneralUtility::writeFileToTypo3tempDir($baseDirectory . '/.bar/..file2', '42');
2662 touch($baseDirectory . '/.bar/.file', '42');
2663 chmod($baseDirectory . '/.bar/.file', 482);
2664 touch($baseDirectory . '/.bar/..file2', '42');
2665 chmod($baseDirectory . '/.bar/..file2', 482);
2666 // Set target permissions and run method
2667 $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
2668 $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0770';
2669 $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($baseDirectory, true);
2670 // Get actual permissions
2671 clearstatcache();
2672 $resultBaseDirectoryPermissions = substr(decoct(fileperms($baseDirectory)), 1);
2673 $resultBaseFilePermissions = substr(decoct(fileperms($baseDirectory . '/file')), 2);
2674 $resultFooDirectoryPermissions = substr(decoct(fileperms($baseDirectory . '/foo')), 1);
2675 $resultFooFilePermissions = substr(decoct(fileperms($baseDirectory . '/foo/file')), 2);
2676 $resultBarDirectoryPermissions = substr(decoct(fileperms($baseDirectory . '/.bar')), 1);
2677 $resultBarFilePermissions = substr(decoct(fileperms($baseDirectory . '/.bar/.file')), 2);
2678 $resultBarFile2Permissions = substr(decoct(fileperms($baseDirectory . '/.bar/..file2')), 2);
2679 // Test if everything was ok
2680 $this->assertTrue($fixPermissionsResult);
2681 $this->assertEquals('0770', $resultBaseDirectoryPermissions);
2682 $this->assertEquals('0660', $resultBaseFilePermissions);
2683 $this->assertEquals('0770', $resultFooDirectoryPermissions);
2684 $this->assertEquals('0660', $resultFooFilePermissions);
2685 $this->assertEquals('0770', $resultBarDirectoryPermissions);
2686 $this->assertEquals('0660', $resultBarFilePermissions);
2687 $this->assertEquals('0660', $resultBarFile2Permissions);
2688 }
2689
2690 /**
2691 * @test
2692 */
2693 public function fixPermissionsDoesNotSetPermissionsToNotAllowedPath()
2694 {
2695 if (TYPO3_OS === 'WIN') {
2696 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2697 }
2698 // Create and prepare test file
2699 $filename = PATH_site . 'typo3temp/var/tests/../../../typo3temp/var/tests/' . $this->getUniqueId('test_');
2700 // Set target permissions and run method
2701 $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
2702 $fixPermissionsResult = GeneralUtility::fixPermissions($filename);
2703 $this->assertFalse($fixPermissionsResult);
2704 }
2705
2706 /**
2707 * @test
2708 */
2709 public function fixPermissionsSetsPermissionsWithRelativeFileReference()
2710 {
2711 if (TYPO3_OS === 'WIN') {
2712 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2713 }
2714 $filename = 'typo3temp/var/tests/' . $this->getUniqueId('test_');
2715 GeneralUtility::writeFileToTypo3tempDir(PATH_site . $filename, '42');
2716 $this->testFilesToDelete[] = PATH_site . $filename;
2717 chmod(PATH_site . $filename, 482);
2718 // Set target permissions and run method
2719 $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
2720 $fixPermissionsResult = GeneralUtility::fixPermissions($filename);
2721 clearstatcache();
2722 $this->assertTrue($fixPermissionsResult);
2723 $this->assertEquals('0660', substr(decoct(fileperms(PATH_site . $filename)), 2));
2724 }
2725
2726 /**
2727 * @test
2728 */
2729 public function fixPermissionsSetsDefaultPermissionsToFile()
2730 {
2731 if (TYPO3_OS === 'WIN') {
2732 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2733 }
2734 $filename = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2735 GeneralUtilityFilesystemFixture::writeFileToTypo3tempDir($filename, '42');
2736 chmod($filename, 482);
2737 unset($GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask']);
2738 $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($filename);
2739 clearstatcache();
2740 $this->assertTrue($fixPermissionsResult);
2741 $this->assertEquals('0644', substr(decoct(fileperms($filename)), 2));
2742 }
2743
2744 /**
2745 * @test
2746 */
2747 public function fixPermissionsSetsDefaultPermissionsToDirectory()
2748 {
2749 if (TYPO3_OS === 'WIN') {
2750 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2751 }
2752 $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2753 GeneralUtilityFilesystemFixture::mkdir($directory);
2754 chmod($directory, 1551);
2755 unset($GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask']);
2756 $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($directory);
2757 clearstatcache();
2758 $this->assertTrue($fixPermissionsResult);
2759 $this->assertEquals('0755', substr(decoct(fileperms($directory)), 1));
2760 }
2761
2762 ///////////////////////////////
2763 // Tests concerning mkdir
2764 ///////////////////////////////
2765 /**
2766 * @test
2767 */
2768 public function mkdirCreatesDirectory()
2769 {
2770 $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2771 $mkdirResult = GeneralUtilityFilesystemFixture::mkdir($directory);
2772 clearstatcache();
2773 $this->assertTrue($mkdirResult);
2774 $this->assertTrue(is_dir($directory));
2775 }
2776
2777 /**
2778 * @test
2779 */
2780 public function mkdirCreatesHiddenDirectory()
2781 {
2782 $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('.test_');
2783 $mkdirResult = GeneralUtilityFilesystemFixture::mkdir($directory);
2784 clearstatcache();
2785 $this->assertTrue($mkdirResult);
2786 $this->assertTrue(is_dir($directory));
2787 }
2788
2789 /**
2790 * @test
2791 */
2792 public function mkdirCreatesDirectoryWithTrailingSlash()
2793 {
2794 $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_') . '/';
2795 $mkdirResult = GeneralUtilityFilesystemFixture::mkdir($directory);
2796 clearstatcache();
2797 $this->assertTrue($mkdirResult);
2798 $this->assertTrue(is_dir($directory));
2799 }
2800
2801 /**
2802 * @test
2803 */
2804 public function mkdirSetsPermissionsOfCreatedDirectory()
2805 {
2806 if (TYPO3_OS === 'WIN') {
2807 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2808 }
2809 $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2810 $oldUmask = umask(19);
2811 $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0772';
2812 GeneralUtilityFilesystemFixture::mkdir($directory);
2813 clearstatcache();
2814 $resultDirectoryPermissions = substr(decoct(fileperms($directory)), 1);
2815 umask($oldUmask);
2816 $this->assertEquals($resultDirectoryPermissions, '0772');
2817 }
2818
2819 /**
2820 * @test
2821 */
2822 public function mkdirSetsGroupOwnershipOfCreatedDirectory()
2823 {
2824 $swapGroup = $this->checkGroups(__FUNCTION__);
2825 if ($swapGroup !== false) {
2826 $GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup'] = $swapGroup;
2827 $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('mkdirtest_');
2828 GeneralUtilityFilesystemFixture::mkdir($directory);
2829 clearstatcache();
2830 $resultDirectoryGroup = filegroup($directory);
2831 $this->assertEquals($resultDirectoryGroup, $swapGroup);
2832 }
2833 }
2834
2835 ///////////////////////////////
2836 // Helper function for filesystem ownership tests
2837 ///////////////////////////////
2838 /**
2839 * Check if test on filesystem group ownership can be done in this environment
2840 * If so, return second group of webserver user
2841 *
2842 * @param string $methodName calling method name
2843 * @return mixed FALSE if test cannot be run, int group id of the second group of webserver user
2844 */
2845 private function checkGroups($methodName)
2846 {
2847 if (TYPO3_OS === 'WIN') {
2848 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2849 return false;
2850 }
2851 if (!function_exists('posix_getegid')) {
2852 $this->markTestSkipped('Function posix_getegid() not available, ' . $methodName . '() tests skipped');
2853 return false;
2854 }
2855 if (posix_getegid() === -1) {
2856 $this->markTestSkipped('Function posix_getegid() returns -1, ' . $methodName . '() tests skipped');
2857 return false;
2858 }
2859 if (!function_exists('posix_getgroups')) {
2860 $this->markTestSkipped('Function posix_getgroups() not available, ' . $methodName . '() tests skipped');
2861 return false;
2862 }
2863 $groups = posix_getgroups();
2864 if (count($groups) <= 1) {
2865 $this->markTestSkipped($methodName . '() test cannot be done when the web server user is only member of 1 group.');
2866 return false;
2867 }
2868 $secondaryGroups = array_diff($groups, [posix_getegid()]);
2869 return array_shift($secondaryGroups);
2870 }
2871
2872 ///////////////////////////////
2873 // Tests concerning mkdir_deep
2874 ///////////////////////////////
2875 /**
2876 * @test
2877 */
2878 public function mkdirDeepCreatesDirectory()
2879 {
2880 $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2881 GeneralUtility::mkdir_deep($directory);
2882 $this->assertTrue(is_dir($directory));
2883 }
2884
2885 /**
2886 * @test
2887 */
2888 public function mkdirDeepCreatesSubdirectoriesRecursive()
2889 {
2890 $directory = $this->getVirtualTestDir() . 'typo3temp/var/tests/' . $this->getUniqueId('test_');
2891 $subDirectory = $directory . '/foo';
2892 GeneralUtility::mkdir_deep($subDirectory);
2893 $this->assertTrue(is_dir($subDirectory));
2894 }
2895
2896 /**
2897 * Data provider for mkdirDeepCreatesDirectoryWithDoubleSlashes.
2898 * @return array
2899 */
2900 public function mkdirDeepCreatesDirectoryWithAndWithoutDoubleSlashesDataProvider()
2901 {
2902 return [
2903 'no double slash if concatenated with PATH_site' => ['fileadmin/testDir1'],
2904 'double slash if concatenated with PATH_site' => ['/fileadmin/testDir2'],
2905 ];
2906 }
2907
2908 /**
2909 * @test
2910 * @dataProvider mkdirDeepCreatesDirectoryWithAndWithoutDoubleSlashesDataProvider
2911 */
2912 public function mkdirDeepCreatesDirectoryWithDoubleSlashes($directoryToCreate)
2913 {
2914 vfsStream::setup();
2915 // Load fixture files and folders from disk
2916 FileStreamWrapper::init(PATH_site);
2917 FileStreamWrapper::registerOverlayPath('fileadmin', 'vfs://root/fileadmin', true);
2918 GeneralUtility::mkdir_deep(PATH_site, $directoryToCreate);
2919 $this->assertTrue(is_dir(PATH_site . $directoryToCreate));
2920 FileStreamWrapper::destroy();
2921 }
2922
2923 /**
2924 * @test
2925 */
2926 public function mkdirDeepFixesPermissionsOfCreatedDirectory()
2927 {
2928 if (TYPO3_OS === 'WIN') {
2929 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2930 }
2931 $directory = $this->getUniqueId('mkdirdeeptest_');
2932 $oldUmask = umask(19);
2933 $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0777';
2934 GeneralUtility::mkdir_deep(PATH_site . 'typo3temp/var/tests/', $directory);
2935 $this->testFilesToDelete[] = PATH_site . 'typo3temp/var/tests/' . $directory;
2936 clearstatcache();
2937 umask($oldUmask);
2938 $this->assertEquals('777', substr(decoct(fileperms(PATH_site . 'typo3temp/var/tests/' . $directory)), -3, 3));
2939 }
2940
2941 /**
2942 * @test
2943 */
2944 public function mkdirDeepFixesPermissionsOnNewParentDirectory()
2945 {
2946 if (TYPO3_OS === 'WIN') {
2947 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2948 }
2949 $directory = $this->getUniqueId('mkdirdeeptest_');
2950 $subDirectory = $directory . '/bar';
2951 $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0777';
2952 $oldUmask = umask(19);
2953 GeneralUtility::mkdir_deep(PATH_site . 'typo3temp/var/tests/', $subDirectory);
2954 $this->testFilesToDelete[] = PATH_site . 'typo3temp/var/tests/' . $directory;
2955 clearstatcache();
2956 umask($oldUmask);
2957 $this->assertEquals('777', substr(decoct(fileperms(PATH_site . 'typo3temp/var/tests/' . $directory)), -3, 3));
2958 }
2959
2960 /**
2961 * @test
2962 */
2963 public function mkdirDeepDoesNotChangePermissionsOfExistingSubDirectories()
2964 {
2965 if (TYPO3_OS === 'WIN') {
2966 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2967 }
2968 $baseDirectory = PATH_site . 'typo3temp/var/tests/';
2969 $existingDirectory = $this->getUniqueId('test_existing_') . '/';
2970 $newSubDirectory = $this->getUniqueId('test_new_');
2971 @mkdir(($baseDirectory . $existingDirectory));
2972 $this->testFilesToDelete[] = $baseDirectory . $existingDirectory;
2973 chmod($baseDirectory . $existingDirectory, 482);
2974 GeneralUtility::mkdir_deep($baseDirectory, $existingDirectory . $newSubDirectory);
2975 $this->assertEquals('0742', substr(decoct(fileperms($baseDirectory . $existingDirectory)), 2));
2976 }
2977
2978 /**
2979 * @test
2980 */
2981 public function mkdirDeepSetsGroupOwnershipOfCreatedDirectory()
2982 {
2983 $swapGroup = $this->checkGroups(__FUNCTION__);
2984 if ($swapGroup !== false) {
2985 $GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup'] = $swapGroup;
2986 $directory = $this->getUniqueId('mkdirdeeptest_');
2987 GeneralUtility::mkdir_deep(PATH_site . 'typo3temp/var/tests/', $directory);
2988 $this->testFilesToDelete[] = PATH_site . 'typo3temp/var/tests/' . $directory;
2989 clearstatcache();
2990 $resultDirectoryGroup = filegroup(PATH_site . 'typo3temp/var/tests/' . $directory);
2991 $this->assertEquals($resultDirectoryGroup, $swapGroup);
2992 }
2993 }
2994
2995 /**
2996 * @test
2997 */
2998 public function mkdirDeepSetsGroupOwnershipOfCreatedParentDirectory()
2999 {
3000 $swapGroup = $this->checkGroups(__FUNCTION__);
3001 if ($swapGroup !== false) {
3002 $GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup'] = $swapGroup;
3003 $directory = $this->getUniqueId('mkdirdeeptest_');
3004 $subDirectory = $directory . '/bar';
3005 GeneralUtility::mkdir_deep(PATH_site . 'typo3temp/var/tests/', $subDirectory);
3006 $this->testFilesToDelete[] = PATH_site . 'typo3temp/var/tests/' . $directory;
3007 clearstatcache();
3008 $resultDirectoryGroup = filegroup(PATH_site . 'typo3temp/var/tests/' . $directory);
3009 $this->assertEquals($resultDirectoryGroup, $swapGroup);
3010 }
3011 }
3012
3013 /**
3014 * @test
3015 */
3016 public function mkdirDeepSetsGroupOwnershipOnNewSubDirectory()
3017 {
3018 $swapGroup = $this->checkGroups(__FUNCTION__);
3019 if ($swapGroup !== false) {
3020 $GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup'] = $swapGroup;
3021 $directory = $this->getUniqueId('mkdirdeeptest_');
3022 $subDirectory = $directory . '/bar';
3023 GeneralUtility::mkdir_deep(PATH_site . 'typo3temp/var/tests/', $subDirectory);
3024 $this->testFilesToDelete[] = PATH_site . 'typo3temp/var/tests/' . $directory;
3025 clearstatcache();
3026 $resultDirectoryGroup = filegroup(PATH_site . 'typo3temp/var/tests/' . $directory);
3027 $this->assertEquals($resultDirectoryGroup, $swapGroup);
3028 }
3029 }
3030
3031 /**
3032 * @test
3033 */
3034 public function mkdirDeepCreatesDirectoryInVfsStream()
3035 {
3036 if (!class_exists('org\\bovigo\\vfs\\vfsStreamWrapper')) {
3037 $this->markTestSkipped('mkdirDeepCreatesDirectoryInVfsStream() test not available with this phpunit version.');
3038 }
3039 vfsStreamWrapper::register();
3040 $baseDirectory = $this->getUniqueId('test_');
3041 vfsStreamWrapper::setRoot(new vfsStreamDirectory($baseDirectory));
3042 GeneralUtility::mkdir_deep('vfs://' . $baseDirectory . '/', 'sub');
3043 $this->assertTrue(is_dir('vfs://' . $baseDirectory . '/sub'));
3044 }
3045
3046 /**
3047 * @test
3048 */
3049 public function mkdirDeepThrowsExceptionIfDirectoryCreationFails()
3050 {
3051 $this->expectException(\RuntimeException::class);
3052 $this->expectExceptionCode(1170251401);
3053
3054 GeneralUtility::mkdir_deep('http://localhost');
3055 }
3056
3057 /**
3058 * @test
3059 */
3060 public function mkdirDeepThrowsExceptionIfBaseDirectoryIsNotOfTypeString()
3061 {
3062 $this->expectException(\InvalidArgumentException::class);
3063 $this->expectExceptionCode(1303662955);
3064
3065 GeneralUtility::mkdir_deep([]);
3066 }
3067
3068 /**
3069 * @test
3070 */
3071 public function mkdirDeepThrowsExceptionIfDeepDirectoryIsNotOfTypeString()
3072 {
3073 $this->expectException(\InvalidArgumentException::class);
3074 $this->expectExceptionCode(1303662956);
3075
3076 GeneralUtility::mkdir_deep(PATH_site . 'typo3temp/foo', []);
3077 }
3078
3079 ///////////////////////////////
3080 // Tests concerning rmdir
3081 ///////////////////////////////
3082
3083 /**
3084 * @test
3085 */
3086 public function rmdirRemovesFile()