[BUGFIX] Properly reset package manager in unit tests
[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 = array();
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 array(
128 'No key parameter' => array(null, array(), array(), null),
129 'Key not found' => array('cake', array(), array(), null),
130 'Value only in GET' => array('cake', array('cake' => 'li\\e'), array(), 'li\\e'),
131 'Value only in POST' => array('cake', array(), array('cake' => 'l\\ie'), 'l\\ie'),
132 'Value from POST preferred over GET' => array('cake', array('cake' => 'is a'), array('cake' => '\\lie'), '\\lie'),
133 'Value can be an array' => array(
134 'cake',
135 array('cake' => array('is a' => 'l\\ie')),
136 array(),
137 array('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 = array('cake' => array('a' => 'is a', 'b' => 'lie'));
164 $postPartData = array('cake' => array('b' => 'lie'));
165 $getPartData = array('cake' => array('a' => 'is a'));
166 $getPartDataModified = array('cake' => array('a' => 'is not a'));
167 return array(
168 'Key doesn\' exist' => array(array('foo'), array('bar'), array()),
169 'No POST data' => array($fullDataArray, array(), $fullDataArray['cake']),
170 'No GET data' => array(array(), $fullDataArray, $fullDataArray['cake']),
171 'POST and GET are merged' => array($getPartData, $postPartData, $fullDataArray['cake']),
172 'POST is preferred over GET' => array($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 array(
188 'Requested input data doesn\'t exist' => array('cake', array(), null),
189 'No key will return entire input data' => array(null, array('cake' => 'l\\ie'), array('cake' => 'l\\ie')),
190 'Can retrieve specific input' => array('cake', array('cake' => 'l\\ie', 'foo'), 'l\\ie'),
191 'Can retrieve nested input data' => array('cake', array('cake' => array('is a' => 'l\\ie')), array('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 = array())
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 array(
237 'No input data used without target key' => array(null, null, array()),
238 'No input data used with target key' => array('', 'cake', array('cake' => '')),
239 'No target key used with string input data' => array('data', null, array()),
240 'No target key used with array input data' => array(array('cake' => 'lie'), null, array('cake' => 'lie')),
241 'Target key and string input data' => array('lie', 'cake', array('cake' => 'lie')),
242 'Replace existing GET data' => array('lie', 'cake', array('cake' => 'lie'), array('cake' => 'is a lie')),
243 'Target key pointing to sublevels and string input data' => array('lie', 'cake|is', array('cake' => array('is' => 'lie'))),
244 'Target key pointing to sublevels and array input data' => array(array('a' => 'lie'), 'cake|is', array('cake' => array('is' => array('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 array(
259 'host with full IP address' => array('127.0.0.1', '127.0.0.1'),
260 'host with two wildcards at the end' => array('127.0.0.1', '127.0.*.*'),
261 'host with wildcard at third octet' => array('127.0.0.1', '127.0.*.1'),
262 'host with wildcard at second octet' => array('127.0.0.1', '127.*.0.1'),
263 '/8 subnet' => array('127.0.0.1', '127.1.1.1/8'),
264 '/32 subnet (match only name)' => array('127.0.0.1', '127.0.0.1/32'),
265 '/30 subnet' => array('10.10.3.1', '10.10.3.3/30'),
266 'host with wildcard in list with IPv4/IPv6 addresses' => array('192.168.1.1', '127.0.0.1, 1234:5678::/126, 192.168.*'),
267 'host in list with IPv4/IPv6 addresses' => array('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 array(
288 'single host' => array('127.0.0.1', '127.0.0.2'),
289 'single host with wildcard' => array('127.0.0.1', '127.*.1.1'),
290 'single host with /32 subnet mask' => array('127.0.0.1', '127.0.0.2/32'),
291 '/31 subnet' => array('127.0.0.1', '127.0.0.2/31'),
292 'list with IPv4/IPv6 addresses' => array('127.0.0.1', '10.0.2.3, 192.168.1.1, ::1'),
293 'list with only IPv6 addresses' => array('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 array(
317 'empty address' => array('::', '::'),
318 'empty with netmask in list' => array('::', '::/0'),
319 'empty with netmask 0 and host-bits set in list' => array('::', '::123/0'),
320 'localhost' => array('::1', '::1'),
321 'localhost with leading zero blocks' => array('::1', '0:0::1'),
322 'host with submask /128' => array('::1', '0:0::1/128'),
323 '/16 subnet' => array('1234::1', '1234:5678::/16'),
324 '/126 subnet' => array('1234:5678::3', '1234:5678::/126'),
325 '/126 subnet with host-bits in list set' => array('1234:5678::3', '1234:5678::2/126'),
326 'list with IPv4/IPv6 addresses' => array('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 array(
347 'empty against localhost' => array('::', '::1'),
348 'empty against localhost with /128 netmask' => array('::', '::1/128'),
349 'localhost against different host' => array('::1', '::2'),
350 'localhost against host with prior bits set' => array('::1', '::1:1'),
351 'host against different /17 subnet' => array('1234::1', '1234:f678::/17'),
352 'host against different /127 subnet' => array('1234:5678::3', '1234:5678::/127'),
353 'host against IPv4 address list' => array('1234:5678::3', '127.0.0.1, 192.168.1.1'),
354 'host against mixed list with IPv6 host in different subnet' => array('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 array(
378 'empty 1' => array('::', str_pad('', 16, "\x00")),
379 'empty 2, already normalized' => array('0000:0000:0000:0000:0000:0000:0000:0000', str_pad('', 16, "\x00")),
380 'already normalized' => array('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' => array('1::2', "\x00\x01" . str_pad('', 12, "\x00") . "\x00\x02"),
382 'expansion in middle 2' => array('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 array(
406 'empty' => array(str_pad('', 16, "\x00"), '::'),
407 'non-empty front' => array("\x01" . str_pad('', 15, "\x00"), '100::'),
408 'non-empty back' => array(str_pad('', 15, "\x00") . "\x01", '::1'),
409 'normalized' => array("\x01\x02\x03\x04" . str_pad('', 8, "\x00") . "\x05\x06\x00\x78", '102:304::506:78'),
410 'expansion in middle 1' => array("\x00\x01" . str_pad('', 12, "\x00") . "\x00\x02", '1::2'),
411 'expansion in middle 2' => array("\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 array(
435 'empty' => array('::', '0000:0000:0000:0000:0000:0000:0000:0000'),
436 'localhost' => array('::1', '0000:0000:0000:0000:0000:0000:0000:0001'),
437 'expansion in middle 1' => array('1::2', '0001:0000:0000:0000:0000:0000:0000:0002'),
438 'expansion in middle 2' => array('1:2::3', '0001:0002:0000:0000:0000:0000:0000:0003'),
439 'expansion in middle 3' => array('1::2:3', '0001:0000:0000:0000:0000:0000:0002:0003'),
440 'expansion in middle 4' => array('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 array(
484 '0.0.0.0' => array('0.0.0.0'),
485 'private IPv4 class C' => array('192.168.0.1'),
486 'private IPv4 class A' => array('10.0.13.1'),
487 'private IPv6' => array('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 array(
508 'null' => array(null),
509 'zero' => array(0),
510 'string' => array('test'),
511 'string empty' => array(''),
512 'string NULL' => array('NULL'),
513 'out of bounds IPv4' => array('300.300.300.300'),
514 'dotted decimal notation with only two dots' => array('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 array(
538 'localhost should usually resolve, IPv4' => array('127.0.0.1', '*'),
539 'localhost should usually resolve, IPv6' => array('::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' => array('aaa.bbb.ccc.ddd.eee', 'aaa.bbb.ccc.ddd.eee'),
543 'aaa.bbb.ccc.ddd.eee, wildcard first' => array('aaa.bbb.ccc.ddd.eee', '*.ccc.ddd.eee'),
544 'aaa.bbb.ccc.ddd.eee, wildcard last' => array('aaa.bbb.ccc.ddd.eee', 'aaa.bbb.ccc.*'),
545 'aaa.bbb.ccc.ddd.eee, wildcard middle' => array('aaa.bbb.ccc.ddd.eee', 'aaa.*.eee'),
546 'list-matches, 1' => array('aaa.bbb.ccc.ddd.eee', 'xxx, yyy, zzz, aaa.*.eee'),
547 'list-matches, 2' => array('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 array(
568 'num-parts of hostname to check can only be less or equal than hostname, 1' => array('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' => array('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 array(
603 'Element as second element of four items' => array('one,findme,three,four'),
604 'Element at beginning of list' => array('findme,one,two'),
605 'Element at end of list' => array('one,two,findme'),
606 'One item list' => array('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 array(
628 'Four item list' => array('one,two,three,four'),
629 'One item list' => array('one'),
630 'Empty list' => array('')
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 array(
656 'Element as second element of three' => array('one,removeme,two', 'one,two'),
657 'Element at beginning of list' => array('removeme,one,two', 'one,two'),
658 'Element at end of list' => array('one,two,removeme', 'one,two'),
659 'One item list' => array('removeme', ''),
660 'Element not contained in list' => array('one,two,three', 'one,two,three'),
661 'Empty element survives' => array('one,,three,,removeme', 'one,,three,'),
662 'Empty element survives at start' => array(',removeme,three,removeme', ',three'),
663 'Empty element survives at end' => array('removeme,three,removeme,', 'three,'),
664 'Empty list' => array('', ''),
665 'List contains removeme multiple times' => array('removeme,notme,removeme,removeme', 'notme'),
666 'List contains removeme multiple times nothing else' => array('removeme,removeme,removeme', ''),
667 'List contains removeme multiple times nothing else 2x' => array('removeme,removeme', ''),
668 'List contains removeme multiple times nothing else 3x' => array('removeme,removeme,removeme', ''),
669 'List contains removeme multiple times nothing else 4x' => array('removeme,removeme,removeme,removeme', ''),
670 'List contains removeme multiple times nothing else 5x' => array('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 array(
696 'Expand for the same number' => array('1,2-2,7', '1,2,7'),
697 'Small range expand with parameters reversed ignores reversed items' => array('1,5-3,7', '1,7'),
698 'Small range expand' => array('1,3-5,7', '1,3,4,5,7'),
699 'Expand at beginning' => array('3-5,1,7', '3,4,5,1,7'),
700 'Expand at end' => array('1,7,3-5', '1,7,3,4,5'),
701 'Multiple small range expands' => array('1,3-5,7-10,12', '1,3,4,5,7,8,9,10,12'),
702 'One item list' => array('1-5', '1,2,3,4,5'),
703 'Nothing to expand' => array('1,2,3,4', '1,2,3,4'),
704 'Empty list' => array('', '')
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 array(
739 'List without duplicates' => array('one,two,three', 'one,two,three'),
740 'List with two consecutive duplicates' => array('one,two,two,three,three', 'one,two,three'),
741 'List with non-consecutive duplicates' => array('one,two,three,two,three', 'one,two,three'),
742 'One item list' => array('one', 'one'),
743 'Empty list' => array('', '')
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 array(
758 'match first part of string' => array('hello world', 'hello'),
759 'match whole string' => array('hello', 'hello'),
760 'integer is part of string with same number' => array('24', 24),
761 'string is part of integer with same number' => array(24, '24'),
762 'integer is part of string starting with same number' => array('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 array(
783 'no string match' => array('hello', 'bye'),
784 'no case sensitive string match' => array('hello world', 'Hello'),
785 'array is not part of string' => array('string', array()),
786 'string is not part of array' => array(array(), 'string'),
787 'NULL is not part of string' => array('string', null),
788 'string is not part of NULL' => array(null, 'string'),
789 'NULL is not part of array' => array(array(), null),
790 'array is not part of NULL' => array(null, array()),
791 'empty string is not part of empty string' => array('', ''),
792 'NULL is not part of empty string' => array('', null),
793 'false is not part of empty string' => array('', false),
794 'empty string is not part of NULL' => array(null, ''),
795 'empty string is not part of false' => array(false, ''),
796 'empty string is not part of zero integer' => array(0, ''),
797 'zero integer is not part of NULL' => array(null, 0),
798 'zero integer is not part of empty string' => array('', 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 array(
831 'IEC Bytes stay bytes (min)' => array(1, '', 0, '1 '),
832 'IEC Bytes stay bytes (max)' => array(921, '', 0, '921 '),
833 'IEC Kilobytes are used (min)' => array(922, '', 0, '0.90 Ki'),
834 'IEC Kilobytes are used (max)' => array(943718, '', 0, '922 Ki'),
835 'IEC Megabytes are used (min)' => array(943719, '', 0, '0.90 Mi'),
836 'IEC Megabytes are used (max)' => array(966367641, '', 0, '922 Mi'),
837 'IEC Gigabytes are used (min)' => array(966367642, '', 0, '0.90 Gi'),
838 'IEC Gigabytes are used (max)' => array(989560464998, '', 0, '922 Gi'),
839 'IEC Decimal is omitted for large kilobytes' => array(31080, '', 0, '30 Ki'),
840 'IEC Decimal is omitted for large megabytes' => array(31458000, '', 0, '30 Mi'),
841 'IEC Decimal is omitted for large gigabytes' => array(32212254720, '', 0, '30 Gi'),
842 'SI Bytes stay bytes (min)' => array(1, 'si', 0, '1 '),
843 'SI Bytes stay bytes (max)' => array(899, 'si', 0, '899 '),
844 'SI Kilobytes are used (min)' => array(901, 'si', 0, '0.90 k'),
845 'SI Kilobytes are used (max)' => array(900000, 'si', 0, '900 k'),
846 'SI Megabytes are used (min)' => array(900001, 'si', 0, '0.90 M'),
847 'SI Megabytes are used (max)' => array(900000000, 'si', 0, '900 M'),
848 'SI Gigabytes are used (min)' => array(900000001, 'si', 0, '0.90 G'),
849 'SI Gigabytes are used (max)' => array(900000000000, 'si', 0, '900 G'),
850 'SI Decimal is omitted for large kilobytes' => array(30000, 'si', 0, '30 k'),
851 'SI Decimal is omitted for large megabytes' => array(30000000, 'si', 0, '30 M'),
852 'SI Decimal is omitted for large gigabytes' => array(30000000000, 'si', 0, '30 G'),
853 'Label for bytes can be exchanged (binary unit)' => array(1, ' Foo|||', 0, '1 Foo'),
854 'Label for kilobytes can be exchanged (binary unit)' => array(1024, '| Foo||', 0, '1.00 Foo'),
855 'Label for megabyes can be exchanged (binary unit)' => array(1048576, '|| Foo|', 0, '1.00 Foo'),
856 'Label for gigabytes can be exchanged (binary unit)' => array(1073741824, '||| Foo', 0, '1.00 Foo'),
857 'Label for bytes can be exchanged (decimal unit)' => array(1, ' Foo|||', 1000, '1 Foo'),
858 'Label for kilobytes can be exchanged (decimal unit)' => array(1000, '| Foo||', 1000, '1.00 Foo'),
859 'Label for megabyes can be exchanged (decimal unit)' => array(1000000, '|| Foo|', 1000, '1.00 Foo'),
860 'Label for gigabytes can be exchanged (decimal unit)' => array(1000000000, '||| Foo', 1000, '1.00 Foo'),
861 'IEC Base is ignored' => array(1024, 'iec', 1000, '1.00 Ki'),
862 'SI Base is ignored' => array(1000, 'si', 1024, '1.00 k'),
863 'Use binary base for unexpected base' => array(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 array(
878 'empty string returns empty array' => array(
879 array(),
880 ''
881 ),
882 'number without operator returns array with plus and number' => array(
883 array(array('+', 42)),
884 '42'
885 ),
886 'two numbers with asterisk return first number with plus and second number with asterisk' => array(
887 array(array('+', 42), array('*', 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 array(
936 'Empty string' => array('', ''),
937 'Double encoded &' => array('&amp;amp;', '&amp;'),
938 'Double encoded numeric entity' => array('&amp;#1234;', '&#1234;'),
939 'Double encoded hexadecimal entity' => array('&amp;#x1b;', '&#x1b;'),
940 'Single encoded entities are not touched' => array('&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 array(
964 'Empty string is not changed' => array('', false, ''),
965 'Normal string is not changed' => array('The cake is a lie √', false, 'The cake is a lie √'),
966 'String with single quotes' => array('The \'cake\' is a lie', false, 'The \\\'cake\\\' is a lie'),
967 'String with single quotes and backslashes - just escape single quotes' => array('The \\\'cake\\\' is a lie', false, 'The \\\\\'cake\\\\\' is a lie'),
968 'String with single quotes and backslashes - escape both' => array('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 array(
1009 'Empty string' => array('', ''),
1010 'String containing only latin characters' => array('the cake is a lie.', 'THE CAKE IS A LIE.'),
1011 'String with umlauts and accent characters' => array('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 array(
1044 'short mail address' => array('a@b.c'),
1045 'simple mail address' => array('test@example.com'),
1046 'uppercase characters' => array('QWERTYUIOPASDFGHJKLZXCVBNM@QWERTYUIOPASDFGHJKLZXCVBNM.NET'),
1047 'equal sign in local part' => array('test=mail@example.com'),
1048 'dash in local part' => array('test-mail@example.com'),
1049 'plus in local part' => array('test+mail@example.com'),
1050 'question mark in local part' => array('test?mail@example.com'),
1051 'slash in local part' => array('foo/bar@example.com'),
1052 'hash in local part' => array('foo#bar@example.com'),
1053 'dot in local part' => array('firstname.lastname@employee.2something.com'),
1054 'dash as local part' => array('-@foo.com'),
1055 'umlauts in domain part' => array('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 array(
1076 'empty string' => array(''),
1077 'empty array' => array(array()),
1078 'integer' => array(42),
1079 'float' => array(42.23),
1080 'array' => array(array('foo')),
1081 'object' => array(new \stdClass()),
1082 '@ sign only' => array('@'),
1083 'string longer than 320 characters' => array(str_repeat('0123456789', 33)),
1084 'duplicate @' => array('test@@example.com'),
1085 'duplicate @ combined with further special characters in local part' => array('test!.!@#$%^&*@example.com'),
1086 'opening parenthesis in local part' => array('foo(bar@example.com'),
1087 'closing parenthesis in local part' => array('foo)bar@example.com'),
1088 'opening square bracket in local part' => array('foo[bar@example.com'),
1089 'closing square bracket as local part' => array(']@example.com'),
1090 'top level domain only' => array('test@com'),
1091 'dash as second level domain' => array('foo@-.com'),
1092 'domain part starting with dash' => array('foo@-foo.com'),
1093 'domain part ending with dash' => array('foo@foo-.com'),
1094 'number as top level domain' => array('foo@bar.123'),
1095 'dot at beginning of domain part' => array('test@.com'),
1096 'local part ends with dot' => array('e.x.a.m.p.l.e.@example.com'),
1097 'umlauts in local part' => array('äöüfoo@bar.com'),
1098 'trailing whitespace' => array('test@example.com '),
1099 'trailing carriage return' => array('test@example.com' . CR),
1100 'trailing linefeed' => array('test@example.com' . LF),
1101 'trailing carriage return linefeed' => array('test@example.com' . CRLF),
1102 'trailing tab' => array('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 = array(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 = array('one' => '√', 'two' => 2);
1141 return array(
1142 'Empty input' => array('foo', array(), ''),
1143 'String parameters' => array('foo', $valueArray, '&foo[one]=%E2%88%9A&foo[two]=2'),
1144 'Nested array parameters' => array('foo', array($valueArray), '&foo[0][one]=%E2%88%9A&foo[0][two]=2'),
1145 'Keep blank parameters' => array('foo', array('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 = array('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 = array('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 ? array($name => $array) : 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 array(
1205 'Empty string' => array('', array()),
1206 'Simple parameter string' => array('&one=%E2%88%9A&two=2', array('one' => '√', 'two' => 2)),
1207 'Nested parameter string' => array('&foo[one]=%E2%88%9A&two=2', array('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 = array('foo' => 1, 'cake' => 'lie');
1221 $expected = array('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 = array('foo' => 1, 'cake' => 'lie');
1234 $expected = array('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 array(
1249 'limit 0 should return unexploded string' => array(
1250 ':',
1251 'my:words:here',
1252 0,
1253 array('my:words:here')
1254 ),
1255 'limit 1 should return unexploded string' => array(
1256 ':',
1257 'my:words:here',
1258 1,
1259 array('my:words:here')
1260 ),
1261 'limit 2 should return two pieces' => array(
1262 ':',
1263 'my:words:here',
1264 2,
1265 array('my:words', 'here')
1266 ),
1267 'limit 3 should return unexploded string' => array(
1268 ':',
1269 'my:words:here',
1270 3,
1271 array('my', 'words', 'here')
1272 ),
1273 'limit 0 should return unexploded string if no delimiter is contained' => array(
1274 ':',
1275 'mywordshere',
1276 0,
1277 array('mywordshere')
1278 ),
1279 'limit 1 should return unexploded string if no delimiter is contained' => array(
1280 ':',
1281 'mywordshere',
1282 1,
1283 array('mywordshere')
1284 ),
1285 'limit 2 should return unexploded string if no delimiter is contained' => array(
1286 ':',
1287 'mywordshere',
1288 2,
1289 array('mywordshere')
1290 ),
1291 'limit 3 should return unexploded string if no delimiter is contained' => array(
1292 ':',
1293 'mywordshere',
1294 3,
1295 array('mywordshere')
1296 ),
1297 'multi character delimiter is handled properly with limit 2' => array(
1298 '[]',
1299 'a[b][c][d]',
1300 2,
1301 array('a[b][c', 'd]')
1302 ),
1303 'multi character delimiter is handled properly with limit 3' => array(
1304 '[]',
1305 'a[b][c][d]',
1306 3,
1307 array('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 = array('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 array(
1581 '100 kilo Bytes' => array('102400', '100k'),
1582 '100 mega Bytes' => array('104857600', '100m'),
1583 '100 giga Bytes' => array('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 array(
1644 'localhost ipv4 without port' => array('127.0.0.1', '127.0.0.1', ''),
1645 'localhost ipv4 with port' => array('127.0.0.1:81', '127.0.0.1', '81'),
1646 'localhost ipv6 without port' => array('[::1]', '[::1]', ''),
1647 'localhost ipv6 with port' => array('[::1]:81', '[::1]', '81'),
1648 'ipv6 without port' => array('[2001:DB8::1]', '[2001:DB8::1]', ''),
1649 'ipv6 with port' => array('[2001:DB8::1]:81', '[2001:DB8::1]', '81'),
1650 'hostname without port' => array('lolli.did.this', 'lolli.did.this', ''),
1651 'hostname with port' => array('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 array(
1681 'hostname without port matching' => array('lolli.did.this', '.*\.did\.this'),
1682 'other hostname without port matching' => array('helmut.did.this', '.*\.did\.this'),
1683 'two different hostnames without port matching 1st host' => array('helmut.is.secure', '(helmut\.is\.secure|lolli\.is\.secure)'),
1684 'two different hostnames without port matching 2nd host' => array('lolli.is.secure', '(helmut\.is\.secure|lolli\.is\.secure)'),
1685 'hostname with port matching' => array('lolli.did.this:42', '.*\.did\.this:42'),
1686 'hostnames are case insensitive 1' => array('lolli.DID.this:42', '.*\.did.this:42'),
1687 'hostnames are case insensitive 2' => array('lolli.did.this:42', '.*\.DID.this:42'),
1688 );
1689 }
1690
1691 /**
1692 * @return array
1693 */
1694 public static function hostnamesNotMatchingTrustedHostsConfigurationDataProvider()
1695 {
1696 return array(
1697 'hostname without port' => array('lolli.did.this', 'helmut\.did\.this'),
1698 'hostname with port, but port not allowed' => array('lolli.did.this:42', 'helmut\.did\.this'),
1699 'two different hostnames in pattern but host header starts with differnet value #1' => array('sub.helmut.is.secure', '(helmut\.is\.secure|lolli\.is\.secure)'),
1700 'two different hostnames in pattern but host header starts with differnet value #2' => array('sub.lolli.is.secure', '(helmut\.is\.secure|lolli\.is\.secure)'),
1701 'two different hostnames in pattern but host header ends with differnet value #1' => array('helmut.is.secure.tld', '(helmut\.is\.secure|lolli\.is\.secure)'),
1702 'two different hostnames in pattern but host header ends with differnet value #2' => array('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 array(
1733 'host value matches server name and server port is default http' => array(
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' => array(
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' => array(
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' => array(
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' => array(
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' => array(
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' => array(
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' => array(
1776 'httpHost' => '[::1]:81',
1777 'serverName' => '[::1]',
1778 'isAllowed' => true,
1779 'serverPort' => '81',
1780 ),
1781 'host value does not match server name' => array(
1782 'httpHost' => 'insecure.web.server',
1783 'serverName' => 'secure.web.server',
1784 'isAllowed' => false,
1785 ),
1786 'host value does not match server port' => array(
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' => array(
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' => array(
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 * @expectedException \UnexpectedValueException
1849 * @expectedExceptionCode 1396795884
1850 */
1851 public function getIndpEnvForHostThrowsExceptionForNotAllowedHostnameValues($httpHost, $hostNamePattern)
1852 {
1853 $_SERVER['HTTP_HOST'] = $httpHost;
1854 $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = $hostNamePattern;
1855 GeneralUtilityFixture::getIndpEnv('HTTP_HOST');
1856 }
1857
1858 /**
1859 * @param string $httpHost HTTP_HOST string
1860 * @param string $hostNamePattern trusted hosts pattern (not used in this test currently)
1861 * @test
1862 * @dataProvider hostnamesNotMatchingTrustedHostsConfigurationDataProvider
1863 */
1864 public function getIndpEnvForHostAllowsAllHostnameValuesIfHostPatternIsSetToAllowAll($httpHost, $hostNamePattern)
1865 {
1866 $_SERVER['HTTP_HOST'] = $httpHost;
1867 $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = GeneralUtility::ENV_TRUSTED_HOSTS_PATTERN_ALLOW_ALL;
1868 $this->assertSame($httpHost, GeneralUtility::getIndpEnv('HTTP_HOST'));
1869 }
1870
1871 /**
1872 * @test
1873 * @dataProvider hostnameAndPortDataProvider
1874 */
1875 public function getIndpEnvTypo3PortParsesHostnamesAndIpAdresses($httpHost, $dummy, $expectedPort)
1876 {
1877 $_SERVER['HTTP_HOST'] = $httpHost;
1878 $this->assertEquals($expectedPort, GeneralUtility::getIndpEnv('TYPO3_PORT'));
1879 }
1880
1881 //////////////////////////////////
1882 // Tests concerning underscoredToUpperCamelCase
1883 //////////////////////////////////
1884 /**
1885 * Data provider for underscoredToUpperCamelCase
1886 *
1887 * @return array expected, input string
1888 */
1889 public function underscoredToUpperCamelCaseDataProvider()
1890 {
1891 return array(
1892 'single word' => array('Blogexample', 'blogexample'),
1893 'multiple words' => array('BlogExample', 'blog_example')
1894 );
1895 }
1896
1897 /**
1898 * @test
1899 * @dataProvider underscoredToUpperCamelCaseDataProvider
1900 */
1901 public function underscoredToUpperCamelCase($expected, $inputString)
1902 {
1903 $this->assertEquals($expected, GeneralUtility::underscoredToUpperCamelCase($inputString));
1904 }
1905
1906 //////////////////////////////////
1907 // Tests concerning underscoredToLowerCamelCase
1908 //////////////////////////////////
1909 /**
1910 * Data provider for underscoredToLowerCamelCase
1911 *
1912 * @return array expected, input string
1913 */
1914 public function underscoredToLowerCamelCaseDataProvider()
1915 {
1916 return array(
1917 'single word' => array('minimalvalue', 'minimalvalue'),
1918 'multiple words' => array('minimalValue', 'minimal_value')
1919 );
1920 }
1921
1922 /**
1923 * @test
1924 * @dataProvider underscoredToLowerCamelCaseDataProvider
1925 */
1926 public function underscoredToLowerCamelCase($expected, $inputString)
1927 {
1928 $this->assertEquals($expected, GeneralUtility::underscoredToLowerCamelCase($inputString));
1929 }
1930
1931 //////////////////////////////////
1932 // Tests concerning camelCaseToLowerCaseUnderscored
1933 //////////////////////////////////
1934 /**
1935 * Data provider for camelCaseToLowerCaseUnderscored
1936 *
1937 * @return array expected, input string
1938 */
1939 public function camelCaseToLowerCaseUnderscoredDataProvider()
1940 {
1941 return array(
1942 'single word' => array('blogexample', 'blogexample'),
1943 'single word starting upper case' => array('blogexample', 'Blogexample'),
1944 'two words starting lower case' => array('minimal_value', 'minimalValue'),
1945 'two words starting upper case' => array('blog_example', 'BlogExample')
1946 );
1947 }
1948
1949 /**
1950 * @test
1951 * @dataProvider camelCaseToLowerCaseUnderscoredDataProvider
1952 */
1953 public function camelCaseToLowerCaseUnderscored($expected, $inputString)
1954 {
1955 $this->assertEquals($expected, GeneralUtility::camelCaseToLowerCaseUnderscored($inputString));
1956 }
1957
1958 //////////////////////////////////
1959 // Tests concerning lcFirst
1960 //////////////////////////////////
1961 /**
1962 * Data provider for lcFirst
1963 *
1964 * @return array expected, input string
1965 */
1966 public function lcfirstDataProvider()
1967 {
1968 return array(
1969 'single word' => array('blogexample', 'blogexample'),
1970 'single Word starting upper case' => array('blogexample', 'Blogexample'),
1971 'two words' => array('blogExample', 'BlogExample')
1972 );
1973 }
1974
1975 /**
1976 * @test
1977 * @dataProvider lcfirstDataProvider
1978 */
1979 public function lcFirst($expected, $inputString)
1980 {
1981 $this->assertEquals($expected, GeneralUtility::lcfirst($inputString));
1982 }
1983
1984 //////////////////////////////////
1985 // Tests concerning isValidUrl
1986 //////////////////////////////////
1987 /**
1988 * Data provider for valid isValidUrl's
1989 *
1990 * @return array Valid resource
1991 */
1992 public function validUrlValidResourceDataProvider()
1993 {
1994 return array(
1995 'http' => array('http://www.example.org/'),
1996 'http without trailing slash' => array('http://qwe'),
1997 'http directory with trailing slash' => array('http://www.example/img/dir/'),
1998 'http directory without trailing slash' => array('http://www.example/img/dir'),
1999 'http index.html' => array('http://example.com/index.html'),
2000 'http index.php' => array('http://www.example.com/index.php'),
2001 'http test.png' => array('http://www.example/img/test.png'),
2002 'http username password querystring and ancher' => array('https://user:pw@www.example.org:80/path?arg=value#fragment'),
2003 'file' => array('file:///tmp/test.c'),
2004 'file directory' => array('file://foo/bar'),
2005 'ftp directory' => array('ftp://ftp.example.com/tmp/'),
2006 'mailto' => array('mailto:foo@bar.com'),
2007 'news' => array('news:news.php.net'),
2008 'telnet' => array('telnet://192.0.2.16:80/'),
2009 'ldap' => array('ldap://[2001:db8::7]/c=GB?objectClass?one'),
2010 'http punycode domain name' => array('http://www.xn--bb-eka.at'),
2011 'http punicode subdomain' => array('http://xn--h-zfa.oebb.at'),
2012 'http domain-name umlauts' => array('http://www.öbb.at'),
2013 'http subdomain umlauts' => array('http://äh.oebb.at'),
2014 );
2015 }
2016
2017 /**
2018 * @test
2019 * @dataProvider validUrlValidResourceDataProvider
2020 */
2021 public function validURLReturnsTrueForValidResource($url)
2022 {
2023 $this->assertTrue(GeneralUtility::isValidUrl($url));
2024 }
2025
2026 /**
2027 * Data provider for invalid isValidUrl's
2028 *
2029 * @return array Invalid ressource
2030 */
2031 public function isValidUrlInvalidRessourceDataProvider()
2032 {
2033 return array(
2034 'http missing colon' => array('http//www.example/wrong/url/'),
2035 'http missing slash' => array('http:/www.example'),
2036 'hostname only' => array('www.example.org/'),
2037 'file missing protocol specification' => array('/tmp/test.c'),
2038 'slash only' => array('/'),
2039 'string http://' => array('http://'),
2040 'string http:/' => array('http:/'),
2041 'string http:' => array('http:'),
2042 'string http' => array('http'),
2043 'empty string' => array(''),
2044 'string -1' => array('-1'),
2045 'string array()' => array('array()'),
2046 'random string' => array('qwe'),
2047 'http directory umlauts' => array('http://www.oebb.at/äöü/'),
2048 );
2049 }
2050
2051 /**
2052 * @test
2053 * @dataProvider isValidUrlInvalidRessourceDataProvider
2054 */
2055 public function validURLReturnsFalseForInvalidRessoure($url)
2056 {
2057 $this->assertFalse(GeneralUtility::isValidUrl($url));
2058 }
2059
2060 //////////////////////////////////
2061 // Tests concerning isOnCurrentHost
2062 //////////////////////////////////
2063 /**
2064 * @test
2065 */
2066 public function isOnCurrentHostReturnsTrueWithCurrentHost()
2067 {
2068 $testUrl = GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL');
2069 $this->assertTrue(GeneralUtility::isOnCurrentHost($testUrl));
2070 }
2071
2072 /**
2073 * Data provider for invalid isOnCurrentHost's
2074 *
2075 * @return array Invalid Hosts
2076 */
2077 public function checkisOnCurrentHostInvalidHosts()
2078 {
2079 return array(
2080 'empty string' => array(''),
2081 'arbitrary string' => array('arbitrary string'),
2082 'localhost IP' => array('127.0.0.1'),
2083 'relative path' => array('./relpath/file.txt'),
2084 'absolute path' => array('/abspath/file.txt?arg=value'),
2085 'differnt host' => array(GeneralUtility::getIndpEnv('TYPO3_REQUEST_HOST') . '.example.org')
2086 );
2087 }
2088
2089 ////////////////////////////////////////
2090 // Tests concerning sanitizeLocalUrl
2091 ////////////////////////////////////////
2092 /**
2093 * Data provider for valid sanitizeLocalUrl paths
2094 *
2095 * @return array Valid url
2096 */
2097 public function sanitizeLocalUrlValidPathsDataProvider()
2098 {
2099 return array(
2100 'alt_intro.php' => array('alt_intro.php'),
2101 'alt_intro.php?foo=1&bar=2' => array('alt_intro.php?foo=1&bar=2'),
2102 '../index.php' => array('../index.php'),
2103 '../typo3/alt_intro.php' => array('../typo3/alt_intro.php'),
2104 '../~userDirectory/index.php' => array('../~userDirectory/index.php'),
2105 '../typo3/index.php?var1=test-case&var2=~user' => array('../typo3/index.php?var1=test-case&var2=~user'),
2106 PATH_site . 'typo3/alt_intro.php' => array(PATH_site . 'typo3/alt_intro.php'),
2107 );
2108 }
2109
2110 /**
2111 * @test
2112 * @param string $path
2113 * @dataProvider sanitizeLocalUrlValidPathsDataProvider
2114 */
2115 public function sanitizeLocalUrlAcceptsNotEncodedValidPaths($path)
2116 {
2117 $this->assertEquals($path, GeneralUtility::sanitizeLocalUrl($path));
2118 }
2119
2120 /**
2121 * @test
2122 * @param string $path
2123 * @dataProvider sanitizeLocalUrlValidPathsDataProvider
2124 */
2125 public function sanitizeLocalUrlAcceptsEncodedValidPaths($path)
2126 {
2127 $this->assertEquals(rawurlencode($path), GeneralUtility::sanitizeLocalUrl(rawurlencode($path)));
2128 }
2129
2130 /**
2131 * Data provider for valid sanitizeLocalUrl's
2132 *
2133 * @return array Valid url
2134 */
2135 public function sanitizeLocalUrlValidUrlsDataProvider()
2136 {
2137 $host = 'localhost';
2138 $subDirectory = '/cms/';
2139
2140 return array(
2141 $subDirectory . 'typo3/alt_intro.php' => array(
2142 $subDirectory . 'typo3/alt_intro.php',
2143 $host,
2144 $subDirectory,
2145 ),
2146 $subDirectory . 'index.php' => array(
2147 $subDirectory . 'index.php',
2148 $host,
2149 $subDirectory,
2150 ),
2151 'http://' . $host . '/typo3/alt_intro.php' => array(
2152 'http://' . $host . '/typo3/alt_intro.php',
2153 $host,
2154 '',
2155 ),
2156 'http://' . $host . $subDirectory . 'typo3/alt_intro.php' => array(
2157 'http://' . $host . $subDirectory . 'typo3/alt_intro.php',
2158 $host,
2159 $subDirectory,
2160 ),
2161 );
2162 }
2163
2164 /**
2165 * @test
2166 * @param string $url
2167 * @param string $host
2168 * @param string $subDirectory
2169 * @dataProvider sanitizeLocalUrlValidUrlsDataProvider
2170 */
2171 public function sanitizeLocalUrlAcceptsNotEncodedValidUrls($url, $host, $subDirectory)
2172 {
2173 $_SERVER['HTTP_HOST'] = $host;
2174 $_SERVER['SCRIPT_NAME'] = $subDirectory . 'typo3/index.php';
2175 GeneralUtility::flushInternalRuntimeCaches();
2176 $this->assertEquals($url, GeneralUtility::sanitizeLocalUrl($url));
2177 }
2178
2179 /**
2180 * @test
2181 * @param string $url
2182 * @param string $host
2183 * @param string $subDirectory
2184 * @dataProvider sanitizeLocalUrlValidUrlsDataProvider
2185 */
2186 public function sanitizeLocalUrlAcceptsEncodedValidUrls($url, $host, $subDirectory)
2187 {
2188 $_SERVER['HTTP_HOST'] = $host;
2189 $_SERVER['SCRIPT_NAME'] = $subDirectory . 'typo3/index.php';
2190 GeneralUtility::flushInternalRuntimeCaches();
2191 $this->assertEquals(rawurlencode($url), GeneralUtility::sanitizeLocalUrl(rawurlencode($url)));
2192 }
2193
2194 /**
2195 * Data provider for invalid sanitizeLocalUrl's
2196 *
2197 * @return array Valid url
2198 */
2199 public function sanitizeLocalUrlInvalidDataProvider()
2200 {
2201 return array(
2202 'empty string' => array(''),
2203 'http domain' => array('http://www.google.de/'),
2204 'https domain' => array('https://www.google.de/'),
2205 'XSS attempt' => array('" onmouseover="alert(123)"'),
2206 'invalid URL, UNC path' => array('\\\\foo\\bar\\'),
2207 'invalid URL, HTML break out attempt' => array('" >blabuubb'),
2208 'base64 encoded string' => array('data:%20text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4='),
2209 );
2210 }
2211
2212 /**
2213 * @test
2214 * @dataProvider sanitizeLocalUrlInvalidDataProvider
2215 */
2216 public function sanitizeLocalUrlDeniesPlainInvalidUrls($url)
2217 {
2218 $this->assertEquals('', GeneralUtility::sanitizeLocalUrl($url));
2219 }
2220
2221 /**
2222 * @test
2223 * @dataProvider sanitizeLocalUrlInvalidDataProvider
2224 */
2225 public function sanitizeLocalUrlDeniesEncodedInvalidUrls($url)
2226 {
2227 $this->assertEquals('', GeneralUtility::sanitizeLocalUrl(rawurlencode($url)));
2228 }
2229
2230 ////////////////////////////////////////
2231 // Tests concerning unlink_tempfile
2232 ////////////////////////////////////////
2233
2234 /**
2235 * @test
2236 */
2237 public function unlink_tempfileRemovesValidFileInTypo3temp()
2238 {
2239 $fixtureFile = __DIR__ . '/Fixtures/clear.gif';
2240 $testFilename = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_') . '.gif';
2241 @copy($fixtureFile, $testFilename);
2242 GeneralUtility::unlink_tempfile($testFilename);
2243 $fileExists = file_exists($testFilename);
2244 $this->assertFalse($fileExists);
2245 }
2246
2247 /**
2248 * @test
2249 */
2250 public function unlink_tempfileRemovesHiddenFile()
2251 {
2252 $fixtureFile = __DIR__ . '/Fixtures/clear.gif';
2253 $testFilename = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('.test_') . '.gif';
2254 @copy($fixtureFile, $testFilename);
2255 GeneralUtility::unlink_tempfile($testFilename);
2256 $fileExists = file_exists($testFilename);
2257 $this->assertFalse($fileExists);
2258 }
2259
2260 /**
2261 * @test
2262 */
2263 public function unlink_tempfileReturnsTrueIfFileWasRemoved()
2264 {
2265 $fixtureFile = __DIR__ . '/Fixtures/clear.gif';
2266 $testFilename = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('test_') . '.gif';
2267 @copy($fixtureFile, $testFilename);
2268 $returnValue = GeneralUtility::unlink_tempfile($testFilename);
2269 $this->assertTrue($returnValue);
2270 }
2271
2272 /**
2273 * @test
2274 */
2275 public function unlink_tempfileReturnsNullIfFileDoesNotExist()
2276 {
2277 $returnValue = GeneralUtility::unlink_tempfile(PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('i_do_not_exist'));
2278 $this->assertNull($returnValue);
2279 }
2280
2281 /**
2282 * @test
2283 */
2284 public function unlink_tempfileReturnsNullIfFileIsNowWithinTypo3temp()
2285 {
2286 $returnValue = GeneralUtility::unlink_tempfile('/tmp/typo3-unit-test-unlink_tempfile');
2287 $this->assertNull($returnValue);
2288 }
2289
2290 //////////////////////////////////////
2291 // Tests concerning tempnam
2292 //////////////////////////////////////
2293
2294 /**
2295 * @test
2296 */
2297 public function tempnamReturnsPathStartingWithGivenPrefix()
2298 {
2299 $filePath = GeneralUtility::tempnam('foo');
2300 $this->testFilesToDelete[] = $filePath;
2301 $fileName = basename($filePath);
2302 $this->assertStringStartsWith('foo', $fileName);
2303 }
2304
2305 /**
2306 * @test
2307 */
2308 public function tempnamReturnsPathWithoutBackslashes()
2309 {
2310 $filePath = GeneralUtility::tempnam('foo');
2311 $this->testFilesToDelete[] = $filePath;
2312 $this->assertNotContains('\\', $filePath);
2313 }
2314
2315 /**
2316 * @test
2317 */
2318 public function tempnamReturnsAbsolutePathInsideDocumentRoot()
2319 {
2320 $filePath = GeneralUtility::tempnam('foo');
2321 $this->testFilesToDelete[] = $filePath;
2322 $this->assertStringStartsWith(PATH_site, $filePath);
2323 }
2324
2325 //////////////////////////////////////
2326 // Tests concerning removeDotsFromTS
2327 //////////////////////////////////////
2328 /**
2329 * @test
2330 */
2331 public function removeDotsFromTypoScriptSucceedsWithDottedArray()
2332 {
2333 $typoScript = array(
2334 'propertyA.' => array(
2335 'keyA.' => array(
2336 'valueA' => 1
2337 ),
2338 'keyB' => 2
2339 ),
2340 'propertyB' => 3
2341 );
2342 $expectedResult = array(
2343 'propertyA' => array(
2344 'keyA' => array(
2345 'valueA' => 1
2346 ),
2347 'keyB' => 2
2348 ),
2349 'propertyB' => 3
2350 );
2351 $this->assertEquals($expectedResult, GeneralUtility::removeDotsFromTS($typoScript));
2352 }
2353
2354 /**
2355 * @test
2356 */
2357 public function removeDotsFromTypoScriptOverridesSubArray()
2358 {
2359 $typoScript = array(
2360 'propertyA.' => array(
2361 'keyA' => 'getsOverridden',
2362 'keyA.' => array(
2363 'valueA' => 1
2364 ),
2365 'keyB' => 2
2366 ),
2367 'propertyB' => 3
2368 );
2369 $expectedResult = array(
2370 'propertyA' => array(
2371 'keyA' => array(
2372 'valueA' => 1
2373 ),
2374 'keyB' => 2
2375 ),
2376 'propertyB' => 3
2377 );
2378 $this->assertEquals($expectedResult, GeneralUtility::removeDotsFromTS($typoScript));
2379 }
2380
2381 /**
2382 * @test
2383 */
2384 public function removeDotsFromTypoScriptOverridesWithScalar()
2385 {
2386 $typoScript = array(
2387 'propertyA.' => array(
2388 'keyA.' => array(
2389 'valueA' => 1
2390 ),
2391 'keyA' => 'willOverride',
2392 'keyB' => 2
2393 ),
2394 'propertyB' => 3
2395 );
2396 $expectedResult = array(
2397 'propertyA' => array(
2398 'keyA' => 'willOverride',
2399 'keyB' => 2
2400 ),
2401 'propertyB' => 3
2402 );
2403 $this->assertEquals($expectedResult, GeneralUtility::removeDotsFromTS($typoScript));
2404 }
2405
2406 //////////////////////////////////////
2407 // Tests concerning get_dirs
2408 //////////////////////////////////////
2409 /**
2410 * @test
2411 */
2412 public function getDirsReturnsArrayOfDirectoriesFromGivenDirectory()
2413 {
2414 $path = PATH_typo3conf;
2415 $directories = GeneralUtility::get_dirs($path);
2416 $this->assertInternalType(\PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $directories);
2417 }
2418
2419 /**
2420 * @test
2421 */
2422 public function getDirsReturnsStringErrorOnPathFailure()
2423 {
2424 $path = 'foo';
2425 $result = GeneralUtility::get_dirs($path);
2426 $expectedResult = 'error';
2427 $this->assertEquals($expectedResult, $result);
2428 }
2429
2430 //////////////////////////////////
2431 // Tests concerning hmac
2432 //////////////////////////////////
2433 /**
2434 * @test
2435 */
2436 public function hmacReturnsHashOfProperLength()
2437 {
2438 $hmac = GeneralUtility::hmac('message');
2439 $this->assertTrue(!empty($hmac) && is_string($hmac));
2440 $this->assertTrue(strlen($hmac) == 40);
2441 }
2442
2443 /**
2444 * @test
2445 */
2446 public function hmacReturnsEqualHashesForEqualInput()
2447 {
2448 $msg0 = 'message';
2449 $msg1 = 'message';
2450 $this->assertEquals(GeneralUtility::hmac($msg0), GeneralUtility::hmac($msg1));
2451 }
2452
2453 /**
2454 * @test
2455 */
2456 public function hmacReturnsNoEqualHashesForNonEqualInput()
2457 {
2458 $msg0 = 'message0';
2459 $msg1 = 'message1';
2460 $this->assertNotEquals(GeneralUtility::hmac($msg0), GeneralUtility::hmac($msg1));
2461 }
2462
2463 //////////////////////////////////
2464 // Tests concerning quoteJSvalue
2465 //////////////////////////////////
2466 /**
2467 * Data provider for quoteJSvalueTest.
2468 *
2469 * @return array
2470 */
2471 public function quoteJsValueDataProvider()
2472 {
2473 return array(
2474 'Immune characters are returned as is' => array(
2475 '._,',
2476 '._,'
2477 ),
2478 'Alphanumerical characters are returned as is' => array(
2479 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
2480 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
2481 ),
2482 'Angle brackets and ampersand are encoded' => array(
2483 '<>&',
2484 '\\u003C\\u003E\\u0026'
2485 ),
2486 'Quotes and backslashes are encoded' => array(
2487 '"\'\\',
2488 '\\u0022\\u0027\\u005C'
2489 ),
2490 'Forward slashes are escaped' => array(
2491 '</script>',
2492 '\\u003C\\/script\\u003E'
2493 ),
2494 'Empty string stays empty' => array(
2495 '',
2496 ''
2497 ),
2498 'Exclamation mark and space are properly encoded' => array(
2499 'Hello World!',
2500 'Hello\\u0020World\\u0021'
2501 ),
2502 'Whitespaces are properly encoded' => array(
2503 TAB . LF . CR . ' ',
2504 '\\u0009\\u000A\\u000D\\u0020'
2505 ),
2506 'Null byte is properly encoded' => array(
2507 chr(0),
2508 '\\u0000'
2509 ),
2510 'Umlauts are properly encoded' => array(
2511 'ÜüÖöÄä',
2512 '\\u00dc\\u00fc\\u00d6\\u00f6\\u00c4\\u00e4'
2513 )
2514 );
2515 }
2516
2517 /**
2518 * @test
2519 * @param string $input
2520 * @param string $expected
2521 * @dataProvider quoteJsValueDataProvider
2522 */
2523 public function quoteJsValueTest($input, $expected)
2524 {
2525 $this->assertSame('\'' . $expected . '\'', GeneralUtility::quoteJSvalue($input));
2526 }
2527
2528 ///////////////////////////////
2529 // Tests concerning _GETset()
2530 ///////////////////////////////
2531 /**
2532 * @test
2533 */
2534 public function getSetWritesArrayToGetSystemVariable()
2535 {
2536 $_GET = array();
2537 $GLOBALS['HTTP_GET_VARS'] = array();
2538 $getParameters = array('foo' => 'bar');
2539 GeneralUtility::_GETset($getParameters);
2540 $this->assertSame($getParameters, $_GET);
2541 }
2542
2543 /**
2544 * @test
2545 */
2546 public function getSetWritesArrayToGlobalsHttpGetVars()
2547 {
2548 $_GET = array();
2549 $GLOBALS['HTTP_GET_VARS'] = array();
2550 $getParameters = array('foo' => 'bar');
2551 GeneralUtility::_GETset($getParameters);
2552 $this->assertSame($getParameters, $GLOBALS['HTTP_GET_VARS']);
2553 }
2554
2555 /**
2556 * @test
2557 */
2558 public function getSetForArrayDropsExistingValues()
2559 {
2560 $_GET = array();
2561 $GLOBALS['HTTP_GET_VARS'] = array();
2562 GeneralUtility::_GETset(array('foo' => 'bar'));
2563 GeneralUtility::_GETset(array('oneKey' => 'oneValue'));
2564 $this->assertEquals(array('oneKey' => 'oneValue'), $GLOBALS['HTTP_GET_VARS']);
2565 }
2566
2567 /**
2568 * @test
2569 */
2570 public function getSetAssignsOneValueToOneKey()
2571 {
2572 $_GET = array();
2573 $GLOBALS['HTTP_GET_VARS'] = array();
2574 GeneralUtility::_GETset('oneValue', 'oneKey');
2575 $this->assertEquals('oneValue', $GLOBALS['HTTP_GET_VARS']['oneKey']);
2576 }
2577
2578 /**
2579 * @test
2580 */
2581 public function getSetForOneValueDoesNotDropUnrelatedValues()
2582 {
2583 $_GET = array();
2584 $GLOBALS['HTTP_GET_VARS'] = array();
2585 GeneralUtility::_GETset(array('foo' => 'bar'));
2586 GeneralUtility::_GETset('oneValue', 'oneKey');
2587 $this->assertEquals(array('foo' => 'bar', 'oneKey' => 'oneValue'), $GLOBALS['HTTP_GET_VARS']);
2588 }
2589
2590 /**
2591 * @test
2592 */
2593 public function getSetCanAssignsAnArrayToASpecificArrayElement()
2594 {
2595 $_GET = array();
2596 $GLOBALS['HTTP_GET_VARS'] = array();
2597 GeneralUtility::_GETset(array('childKey' => 'oneValue'), 'parentKey');
2598 $this->assertEquals(array('parentKey' => array('childKey' => 'oneValue')), $GLOBALS['HTTP_GET_VARS']);
2599 }
2600
2601 /**
2602 * @test
2603 */
2604 public function getSetCanAssignAStringValueToASpecificArrayChildElement()
2605 {
2606 $_GET = array();
2607 $GLOBALS['HTTP_GET_VARS'] = array();
2608 GeneralUtility::_GETset('oneValue', 'parentKey|childKey');
2609 $this->assertEquals(array('parentKey' => array('childKey' => 'oneValue')), $GLOBALS['HTTP_GET_VARS']);
2610 }
2611
2612 /**
2613 * @test
2614 */
2615 public function getSetCanAssignAnArrayToASpecificArrayChildElement()
2616 {
2617 $_GET = array();
2618 $GLOBALS['HTTP_GET_VARS'] = array();
2619 GeneralUtility::_GETset(array('key1' => 'value1', 'key2' => 'value2'), 'parentKey|childKey');
2620 $this->assertEquals(array(
2621 'parentKey' => array(
2622 'childKey' => array('key1' => 'value1', 'key2' => 'value2')
2623 )
2624 ), $GLOBALS['HTTP_GET_VARS']);
2625 }
2626
2627 ///////////////////////////
2628 // Tests concerning minifyJavaScript
2629 ///////////////////////////
2630 /**
2631 * @test
2632 */
2633 public function minifyJavaScriptReturnsInputStringIfNoHookIsRegistered()
2634 {
2635 unset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['minifyJavaScript']);
2636 $testString = $this->getUniqueId('string');
2637 $this->assertSame($testString, GeneralUtility::minifyJavaScript($testString));
2638 }
2639
2640 /**
2641 * Create an own hook callback class, register as hook, and check
2642 * if given string to compress is given to hook method
2643 *
2644 * @test
2645 */
2646 public function minifyJavaScriptCallsRegisteredHookWithInputString()
2647 {
2648 $hookClassName = $this->getUniqueId('tx_coretest');
2649 $minifyHookMock = $this->getMock('stdClass', array('minify'), array(), $hookClassName);
2650 $functionName = $hookClassName . '->minify';
2651 $GLOBALS['T3_VAR']['callUserFunction'][$functionName] = array();
2652 $GLOBALS['T3_VAR']['callUserFunction'][$functionName]['obj'] = $minifyHookMock;
2653 $GLOBALS['T3_VAR']['callUserFunction'][$functionName]['method'] = 'minify';
2654 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['minifyJavaScript'][] = $functionName;
2655 $minifyHookMock->expects($this->once())->method('minify')->will($this->returnCallback(array($this, 'isMinifyJavaScriptHookCalledCallback')));
2656 GeneralUtility::minifyJavaScript('foo');
2657 }
2658
2659 /**
2660 * Callback function used in minifyJavaScriptCallsRegisteredHookWithInputString test
2661 *
2662 * @param array $params
2663 */
2664 public function isMinifyJavaScriptHookCalledCallback(array $params)
2665 {
2666 // We can not throw an exception here, because that would be caught by the
2667 // minifyJavaScript method under test itself. Thus, we just die if the
2668 // input string is not ok.
2669 if ($params['script'] !== 'foo') {
2670 die('broken');
2671 }
2672 }
2673
2674 /**
2675 * Create a hook callback, use callback to throw an exception and check
2676 * if the exception is given as error parameter to the calling method.
2677 *
2678 * @test
2679 */
2680 public function minifyJavaScriptReturnsErrorStringOfHookException()
2681 {
2682 $hookClassName = $this->getUniqueId('tx_coretest');
2683 $minifyHookMock = $this->getMock('stdClass', array('minify'), array(), $hookClassName);
2684 $functionName = '&' . $hookClassName . '->minify';
2685 $GLOBALS['T3_VAR']['callUserFunction'][$functionName] = array();
2686 $GLOBALS['T3_VAR']['callUserFunction'][$functionName]['obj'] = $minifyHookMock;
2687 $GLOBALS['T3_VAR']['callUserFunction'][$functionName]['method'] = 'minify';
2688 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['minifyJavaScript'][] = $functionName;
2689 $minifyHookMock->expects($this->any())->method('minify')->will($this->returnCallback(array($this, 'minifyJavaScriptErroneousCallback')));
2690 $error = '';
2691 GeneralUtility::minifyJavaScript('string to compress', $error);
2692 $this->assertSame('Error minifying java script: foo', $error);
2693 }
2694
2695 /**
2696 * Check if the error message that is returned by the hook callback
2697 * is logged to \TYPO3\CMS\Core\GeneralUtility::devLog.
2698 *
2699 * @test
2700 */
2701 public function minifyJavaScriptWritesExceptionMessageToDevLog()
2702 {
2703 $hookClassName = $this->getUniqueId('tx_coretest');
2704 $minifyHookMock = $this->getMock('stdClass', array('minify'), array(), $hookClassName);
2705 $functionName = '&' . $hookClassName . '->minify';
2706 $GLOBALS['T3_VAR']['callUserFunction'][$functionName] = array();
2707 $GLOBALS['T3_VAR']['callUserFunction'][$functionName]['obj'] = $minifyHookMock;
2708 $GLOBALS['T3_VAR']['callUserFunction'][$functionName]['method'] = 'minify';
2709 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['minifyJavaScript'][] = $functionName;
2710 $minifyHookMock->expects($this->any())->method('minify')->will($this->returnCallback(array($this, 'minifyJavaScriptErroneousCallback')));
2711 $this->expectException(\RuntimeException::class);
2712 GeneralUtilityMinifyJavaScriptFixture::minifyJavaScript('string to compress');
2713 }
2714
2715 /**
2716 * Callback function used in
2717 * minifyJavaScriptReturnsErrorStringOfHookException and
2718 * minifyJavaScriptWritesExceptionMessageToDevLog
2719 *
2720 * @throws \RuntimeException
2721 */
2722 public function minifyJavaScriptErroneousCallback()
2723 {
2724 throw new \RuntimeException('foo', 1344888548);
2725 }
2726
2727 ///////////////////////////////
2728 // Tests concerning fixPermissions
2729 ///////////////////////////////
2730 /**
2731 * @test
2732 */
2733 public function fixPermissionsSetsGroup()
2734 {
2735 if (TYPO3_OS === 'WIN') {
2736 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2737 }
2738 if (!function_exists('posix_getegid')) {
2739 $this->markTestSkipped('Function posix_getegid() not available, fixPermissionsSetsGroup() tests skipped');
2740 }
2741 if (posix_getegid() === -1) {
2742 $this->markTestSkipped('The fixPermissionsSetsGroup() is not available on Mac OS because posix_getegid() always returns -1 on Mac OS.');
2743 }
2744 // Create and prepare test file
2745 $filename = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2746 GeneralUtilityFilesystemFixture::writeFileToTypo3tempDir($filename, '42');
2747 $currentGroupId = posix_getegid();
2748 // Set target group and run method
2749 $GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup'] = $currentGroupId;
2750 GeneralUtilityFilesystemFixture::fixPermissions($filename);
2751 clearstatcache();
2752 $this->assertEquals($currentGroupId, filegroup($filename));
2753 }
2754
2755 /**
2756 * @test
2757 */
2758 public function fixPermissionsSetsPermissionsToFile()
2759 {
2760 if (TYPO3_OS === 'WIN') {
2761 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2762 }
2763 // Create and prepare test file
2764 $filename = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2765 GeneralUtilityFilesystemFixture::writeFileToTypo3tempDir($filename, '42');
2766 chmod($filename, 482);
2767 // Set target permissions and run method
2768 $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
2769 $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($filename);
2770 clearstatcache();
2771 $this->assertTrue($fixPermissionsResult);
2772 $this->assertEquals('0660', substr(decoct(fileperms($filename)), 2));
2773 }
2774
2775 /**
2776 * @test
2777 */
2778 public function fixPermissionsSetsPermissionsToHiddenFile()
2779 {
2780 if (TYPO3_OS === 'WIN') {
2781 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2782 }
2783 // Create and prepare test file
2784 $filename = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2785 GeneralUtilityFilesystemFixture::writeFileToTypo3tempDir($filename, '42');
2786 chmod($filename, 482);
2787 // Set target permissions and run method
2788 $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
2789 $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($filename);
2790 clearstatcache();
2791 $this->assertTrue($fixPermissionsResult);
2792 $this->assertEquals('0660', substr(decoct(fileperms($filename)), 2));
2793 }
2794
2795 /**
2796 * @test
2797 */
2798 public function fixPermissionsSetsPermissionsToDirectory()
2799 {
2800 if (TYPO3_OS === 'WIN') {
2801 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2802 }
2803 // Create and prepare test directory
2804 $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2805 GeneralUtilityFilesystemFixture::mkdir($directory);
2806 chmod($directory, 1551);
2807 // Set target permissions and run method
2808 $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0770';
2809 $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($directory);
2810 clearstatcache();
2811 $this->assertTrue($fixPermissionsResult);
2812 $this->assertEquals('0770', substr(decoct(fileperms($directory)), 1));
2813 }
2814
2815 /**
2816 * @test
2817 */
2818 public function fixPermissionsSetsPermissionsToDirectoryWithTrailingSlash()
2819 {
2820 if (TYPO3_OS === 'WIN') {
2821 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2822 }
2823 // Create and prepare test directory
2824 $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2825 GeneralUtilityFilesystemFixture::mkdir($directory);
2826 chmod($directory, 1551);
2827 // Set target permissions and run method
2828 $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0770';
2829 $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($directory . '/');
2830 // Get actual permissions and clean up
2831 clearstatcache();
2832 $this->assertTrue($fixPermissionsResult);
2833 $this->assertEquals('0770', substr(decoct(fileperms($directory)), 1));
2834 }
2835
2836 /**
2837 * @test
2838 */
2839 public function fixPermissionsSetsPermissionsToHiddenDirectory()
2840 {
2841 if (TYPO3_OS === 'WIN') {
2842 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2843 }
2844 // Create and prepare test directory
2845 $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2846 GeneralUtilityFilesystemFixture::mkdir($directory);
2847 chmod($directory, 1551);
2848 // Set target permissions and run method
2849 $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0770';
2850 $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($directory);
2851 // Get actual permissions and clean up
2852 clearstatcache();
2853 $this->assertTrue($fixPermissionsResult);
2854 $this->assertEquals('0770', substr(decoct(fileperms($directory)), 1));
2855 }
2856
2857 /**
2858 * @test
2859 */
2860 public function fixPermissionsCorrectlySetsPermissionsRecursive()
2861 {
2862 if (TYPO3_OS === 'WIN') {
2863 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2864 }
2865 // Create and prepare test directory and file structure
2866 $baseDirectory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2867 GeneralUtilityFilesystemFixture::mkdir($baseDirectory);
2868 chmod($baseDirectory, 1751);
2869 GeneralUtilityFilesystemFixture::writeFileToTypo3tempDir($baseDirectory . '/file', '42');
2870 chmod($baseDirectory . '/file', 482);
2871 GeneralUtilityFilesystemFixture::mkdir($baseDirectory . '/foo');
2872 chmod($baseDirectory . '/foo', 1751);
2873 GeneralUtilityFilesystemFixture::writeFileToTypo3tempDir($baseDirectory . '/foo/file', '42');
2874 chmod($baseDirectory . '/foo/file', 482);
2875 GeneralUtilityFilesystemFixture::mkdir($baseDirectory . '/.bar');
2876 chmod($baseDirectory . '/.bar', 1751);
2877 // Use this if writeFileToTypo3tempDir is fixed to create hidden files in subdirectories
2878 // \TYPO3\CMS\Core\Utility\GeneralUtility::writeFileToTypo3tempDir($baseDirectory . '/.bar/.file', '42');
2879 // \TYPO3\CMS\Core\Utility\GeneralUtility::writeFileToTypo3tempDir($baseDirectory . '/.bar/..file2', '42');
2880 touch($baseDirectory . '/.bar/.file', '42');
2881 chmod($baseDirectory . '/.bar/.file', 482);
2882 touch($baseDirectory . '/.bar/..file2', '42');
2883 chmod($baseDirectory . '/.bar/..file2', 482);
2884 // Set target permissions and run method
2885 $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
2886 $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] = '0770';
2887 $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($baseDirectory, true);
2888 // Get actual permissions
2889 clearstatcache();
2890 $resultBaseDirectoryPermissions = substr(decoct(fileperms($baseDirectory)), 1);
2891 $resultBaseFilePermissions = substr(decoct(fileperms($baseDirectory . '/file')), 2);
2892 $resultFooDirectoryPermissions = substr(decoct(fileperms($baseDirectory . '/foo')), 1);
2893 $resultFooFilePermissions = substr(decoct(fileperms($baseDirectory . '/foo/file')), 2);
2894 $resultBarDirectoryPermissions = substr(decoct(fileperms($baseDirectory . '/.bar')), 1);
2895 $resultBarFilePermissions = substr(decoct(fileperms($baseDirectory . '/.bar/.file')), 2);
2896 $resultBarFile2Permissions = substr(decoct(fileperms($baseDirectory . '/.bar/..file2')), 2);
2897 // Test if everything was ok
2898 $this->assertTrue($fixPermissionsResult);
2899 $this->assertEquals('0770', $resultBaseDirectoryPermissions);
2900 $this->assertEquals('0660', $resultBaseFilePermissions);
2901 $this->assertEquals('0770', $resultFooDirectoryPermissions);
2902 $this->assertEquals('0660', $resultFooFilePermissions);
2903 $this->assertEquals('0770', $resultBarDirectoryPermissions);
2904 $this->assertEquals('0660', $resultBarFilePermissions);
2905 $this->assertEquals('0660', $resultBarFile2Permissions);
2906 }
2907
2908 /**
2909 * @test
2910 */
2911 public function fixPermissionsDoesNotSetPermissionsToNotAllowedPath()
2912 {
2913 if (TYPO3_OS === 'WIN') {
2914 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2915 }
2916 // Create and prepare test file
2917 $filename = PATH_site . 'typo3temp/var/tests/../../../typo3temp/var/tests/' . $this->getUniqueId('test_');
2918 // Set target permissions and run method
2919 $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
2920 $fixPermissionsResult = GeneralUtility::fixPermissions($filename);
2921 $this->assertFalse($fixPermissionsResult);
2922 }
2923
2924 /**
2925 * @test
2926 */
2927 public function fixPermissionsSetsPermissionsWithRelativeFileReference()
2928 {
2929 if (TYPO3_OS === 'WIN') {
2930 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2931 }
2932 $filename = 'typo3temp/var/tests/' . $this->getUniqueId('test_');
2933 GeneralUtility::writeFileToTypo3tempDir(PATH_site . $filename, '42');
2934 $this->testFilesToDelete[] = PATH_site . $filename;
2935 chmod(PATH_site . $filename, 482);
2936 // Set target permissions and run method
2937 $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = '0660';
2938 $fixPermissionsResult = GeneralUtility::fixPermissions($filename);
2939 clearstatcache();
2940 $this->assertTrue($fixPermissionsResult);
2941 $this->assertEquals('0660', substr(decoct(fileperms(PATH_site . $filename)), 2));
2942 }
2943
2944 /**
2945 * @test
2946 */
2947 public function fixPermissionsSetsDefaultPermissionsToFile()
2948 {
2949 if (TYPO3_OS === 'WIN') {
2950 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2951 }
2952 $filename = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2953 GeneralUtilityFilesystemFixture::writeFileToTypo3tempDir($filename, '42');
2954 chmod($filename, 482);
2955 unset($GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask']);
2956 $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($filename);
2957 clearstatcache();
2958 $this->assertTrue($fixPermissionsResult);
2959 $this->assertEquals('0644', substr(decoct(fileperms($filename)), 2));
2960 }
2961
2962 /**
2963 * @test
2964 */
2965 public function fixPermissionsSetsDefaultPermissionsToDirectory()
2966 {
2967 if (TYPO3_OS === 'WIN') {
2968 $this->markTestSkipped(self::NO_FIX_PERMISSIONS_ON_WINDOWS);
2969 }
2970 $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2971 GeneralUtilityFilesystemFixture::mkdir($directory);
2972 chmod($directory, 1551);
2973 unset($GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask']);
2974 $fixPermissionsResult = GeneralUtilityFilesystemFixture::fixPermissions($directory);
2975 clearstatcache();
2976 $this->assertTrue($fixPermissionsResult);
2977 $this->assertEquals('0755', substr(decoct(fileperms($directory)), 1));
2978 }
2979
2980 ///////////////////////////////
2981 // Tests concerning mkdir
2982 ///////////////////////////////
2983 /**
2984 * @test
2985 */
2986 public function mkdirCreatesDirectory()
2987 {
2988 $directory = $this->getVirtualTestDir() . '/' . $this->getUniqueId('test_');
2989 $mkdirResult = GeneralUtilityFilesystemFixture::mkdir($directory);
2990 clearstatcache();
2991 $this->assertTrue($mkdirResult);
2992 $this->assertTrue(is_dir($directory));
2993 }
2994
2995 /**
2996 * @test
2997 */
2998 public function mkdirCreatesHiddenDirectory()
2999 {
3000 $directory = $this->getVirtualTestDir