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