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