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