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