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