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