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