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