[FEATURE] Install tool: Remove obsolete keys from LocalConfiguration
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Utility / ArrayUtilityTest.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Unit\Utility;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2011-2013 Susanne Moog <typo3@susanne-moog.de>
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 *
19 * This script is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * This copyright notice MUST APPEAR in all copies of the script!
25 ***************************************************************/
26
27 use \TYPO3\CMS\Core\Utility\ArrayUtility;
28
29 /**
30 * Test case
31 *
32 * @author Susanne Moog <typo3@susanne-moog.de>
33 * @author Christian Kuhn <lolli@schwarzbu.ch>
34 */
35 class ArrayUtilityTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
36
37 ///////////////////////
38 // Tests concerning filterByValueRecursive
39 ///////////////////////
40 /**
41 * Data provider for filterByValueRecursiveCorrectlyFiltersArray
42 *
43 * Every array splits into:
44 * - String value to search for
45 * - Input array
46 * - Expected result array
47 */
48 public function filterByValueRecursive() {
49 return array(
50 'empty search array' => array(
51 'banana',
52 array(),
53 array()
54 ),
55 'empty string as needle' => array(
56 '',
57 array(
58 '',
59 'apple'
60 ),
61 array(
62 ''
63 )
64 ),
65 'flat array searching for string' => array(
66 'banana',
67 array(
68 'apple',
69 'banana'
70 ),
71 array(
72 1 => 'banana'
73 )
74 ),
75 'flat array searching for string with two matches' => array(
76 'banana',
77 array(
78 'foo' => 'apple',
79 'firstbanana' => 'banana',
80 'secondbanana' => 'banana'
81 ),
82 array(
83 'firstbanana' => 'banana',
84 'secondbanana' => 'banana'
85 )
86 ),
87 'multi dimensional array searching for string with multiple matches' => array(
88 'banana',
89 array(
90 'foo' => 'apple',
91 'firstbanana' => 'banana',
92 'grape' => array(
93 'foo2' => 'apple2',
94 'secondbanana' => 'banana',
95 'foo3' => array()
96 ),
97 'bar' => 'orange'
98 ),
99 array(
100 'firstbanana' => 'banana',
101 'grape' => array(
102 'secondbanana' => 'banana'
103 )
104 )
105 ),
106 'multi dimensional array searching for integer with multiple matches' => array(
107 42,
108 array(
109 'foo' => 23,
110 'bar' => 42,
111 array(
112 'foo' => 23,
113 'bar' => 42
114 )
115 ),
116 array(
117 'bar' => 42,
118 array(
119 'bar' => 42
120 )
121 )
122 ),
123 'flat array searching for boolean TRUE' => array(
124 TRUE,
125 array(
126 23 => FALSE,
127 42 => TRUE
128 ),
129 array(
130 42 => TRUE
131 )
132 ),
133 'multi dimensional array searching for boolean FALSE' => array(
134 FALSE,
135 array(
136 23 => FALSE,
137 42 => TRUE,
138 'foo' => array(
139 23 => FALSE,
140 42 => TRUE
141 )
142 ),
143 array(
144 23 => FALSE,
145 'foo' => array(
146 23 => FALSE
147 )
148 )
149 ),
150 'flat array searching for array' => array(
151 array(
152 'foo' => 'bar'
153 ),
154 array(
155 'foo' => 'bar',
156 'foobar' => array(
157 'foo' => 'bar'
158 )
159 ),
160 array(
161 'foobar' => array(
162 'foo' => 'bar'
163 )
164 )
165 )
166 );
167 }
168
169 /**
170 * @test
171 * @dataProvider filterByValueRecursive
172 */
173 public function filterByValueRecursiveCorrectlyFiltersArray($needle, $haystack, $expectedResult) {
174 $this->assertEquals(
175 $expectedResult,
176 ArrayUtility::filterByValueRecursive($needle, $haystack)
177 );
178 }
179
180 /**
181 * @test
182 */
183 public function filterByValueRecursiveMatchesReferencesToSameObject() {
184 $instance = new \stdClass();
185 $this->assertEquals(
186 array($instance),
187 ArrayUtility::filterByValueRecursive($instance, array($instance))
188 );
189 }
190
191 /**
192 * @test
193 */
194 public function filterByValueRecursiveDoesNotMatchDifferentInstancesOfSameClass() {
195 $this->assertEquals(
196 array(),
197 ArrayUtility::filterByValueRecursive(new \stdClass(), array(new \stdClass()))
198 );
199 }
200
201 ///////////////////////
202 // Tests concerning isValidPath
203 ///////////////////////
204 /**
205 * Mock the class under test, isValidPath() (method under test), calls
206 * static getValuePath() internally, which is mocked here to return a specific
207 * result. This works because of 'static' keyword' instead of 'self'
208 * for getValueByPath() call, using late static binding in PHP 5.3
209 *
210 * @test
211 */
212 public function isValidPathReturnsTrueIfPathExists() {
213 $namespace = 'TYPO3\\CMS\\Core\\Utility';
214 $className = uniqid('ArrayUtility');
215 eval(
216 'namespace ' . $namespace . ';' .
217 'class ' . $className . ' extends \\TYPO3\\CMS\\Core\\Utility\\ArrayUtility {' .
218 ' public static function getValueByPath() {' .
219 ' return 42;' .
220 ' }' .
221 '}'
222 );
223 $className = $namespace . '\\' . $className;
224 $this->assertTrue($className::isValidPath(array('foo'), 'foo'));
225 }
226
227 /**
228 * @test
229 */
230 public function isValidPathReturnsFalseIfPathDoesNotExist() {
231 $namespace = 'TYPO3\\CMS\\Core\\Utility';
232 $className = uniqid('ArrayUtility');
233 eval(
234 'namespace ' . $namespace . ';' .
235 'class ' . $className . ' extends \\TYPO3\\CMS\\Core\\Utility\\ArrayUtility {' .
236 ' public static function getValueByPath() {' .
237 ' throw new \RuntimeException(\'foo\', 123);' .
238 ' }' .
239 '}'
240 );
241 $className = $namespace . '\\' . $className;
242 $this->assertFalse($className::isValidPath(array('foo'), 'foo'));
243 }
244
245 ///////////////////////
246 // Tests concerning getValueByPath
247 ///////////////////////
248 /**
249 * @test
250 * @expectedException \RuntimeException
251 */
252 public function getValueByPathThrowsExceptionIfPathIsEmpty() {
253 ArrayUtility::getValueByPath(array(), '');
254 }
255
256 /**
257 * Data provider for getValueByPathThrowsExceptionIfPathNotExists
258 * Every array splits into:
259 * - Array to get value from
260 * - String path
261 * - Expected result
262 */
263 public function getValueByPathInvalidPathDataProvider() {
264 return array(
265 'not existing path 1' => array(
266 array(
267 'foo' => array()
268 ),
269 'foo/bar/baz',
270 FALSE
271 ),
272 'not existing path 2' => array(
273 array(
274 'foo' => array(
275 'baz' => 42
276 ),
277 'bar' => array()
278 ),
279 'foo/bar/baz',
280 FALSE
281 ),
282 // Negative test: This could be improved and the test moved to
283 // the valid data provider if the method supports this
284 'doubletick encapsulated quoted doubletick does not work' => array(
285 array(
286 '"foo"bar"' => array(
287 'baz' => 42
288 ),
289 'bar' => array()
290 ),
291 '"foo\\"bar"/baz',
292 42
293 ),
294 // Negative test: Method could be improved here
295 'path with doubletick does not work' => array(
296 array(
297 'fo"o' => array(
298 'bar' => 42
299 )
300 ),
301 'fo"o/foobar',
302 42
303 )
304 );
305 }
306
307 /**
308 * @test
309 * @dataProvider getValueByPathInvalidPathDataProvider
310 * @expectedException \RuntimeException
311 */
312 public function getValueByPathThrowsExceptionIfPathNotExists(array $array, $path) {
313 ArrayUtility::getValueByPath($array, $path);
314 }
315
316 /**
317 * Data provider for getValueByPathReturnsCorrectValue
318 * Every array splits into:
319 * - Array to get value from
320 * - String path
321 * - Expected result
322 */
323 public function getValueByPathValidDataProvider() {
324 $testObject = new \StdClass();
325 $testObject->foo = 'foo';
326 $testObject->bar = 'bar';
327 return array(
328 'integer in multi level array' => array(
329 array(
330 'foo' => array(
331 'bar' => array(
332 'baz' => 42
333 ),
334 'bar2' => array()
335 )
336 ),
337 'foo/bar/baz',
338 42
339 ),
340 'zero integer in multi level array' => array(
341 array(
342 'foo' => array(
343 'bar' => array(
344 'baz' => 0
345 )
346 )
347 ),
348 'foo/bar/baz',
349 0
350 ),
351 'NULL value in multi level array' => array(
352 array(
353 'foo' => array(
354 'baz' => NULL
355 )
356 ),
357 'foo/baz',
358 NULL
359 ),
360 'get string value' => array(
361 array(
362 'foo' => array(
363 'baz' => 'this is a test string'
364 )
365 ),
366 'foo/baz',
367 'this is a test string'
368 ),
369 'get boolean value: FALSE' => array(
370 array(
371 'foo' => array(
372 'baz' => FALSE
373 )
374 ),
375 'foo/baz',
376 FALSE
377 ),
378 'get boolean value: TRUE' => array(
379 array(
380 'foo' => array(
381 'baz' => TRUE
382 )
383 ),
384 'foo/baz',
385 TRUE
386 ),
387 'get object value' => array(
388 array(
389 'foo' => array(
390 'baz' => $testObject
391 )
392 ),
393 'foo/baz',
394 $testObject
395 ),
396 'enclosed path' => array(
397 array(
398 'foo/bar' => array(
399 'foobar' => 42
400 )
401 ),
402 '"foo/bar"/foobar',
403 42
404 )
405 );
406 }
407
408 /**
409 * @test
410 * @dataProvider getValueByPathValidDataProvider
411 */
412 public function getValueByPathGetsCorrectValue(array $array, $path, $expectedResult) {
413 $this->assertEquals($expectedResult, ArrayUtility::getValueByPath($array, $path));
414 }
415
416 /**
417 * @test
418 */
419 public function getValueByPathAccpetsDifferentDelimeter() {
420 $input = array(
421 'foo' => array(
422 'bar' => array(
423 'baz' => 42
424 ),
425 'bar2' => array()
426 )
427 );
428 $searchPath = 'foo%bar%baz';
429 $expected = 42;
430 $delimeter = '%';
431 $this->assertEquals(
432 $expected,
433 ArrayUtility::getValueByPath($input, $searchPath, $delimeter)
434 );
435 }
436
437 ///////////////////////
438 // Tests concerning setValueByPath
439 ///////////////////////
440 /**
441 * @test
442 * @expectedException \RuntimeException
443 */
444 public function setValueByPathThrowsExceptionIfPathIsEmpty() {
445 ArrayUtility::setValueByPath(array(), '', NULL);
446 }
447
448 /**
449 * @test
450 * @expectedException \RuntimeException
451 */
452 public function setValueByPathThrowsExceptionIfPathIsNotAString() {
453 ArrayUtility::setValueByPath(array(), array('foo'), NULL);
454 }
455
456 /**
457 * Data provider for setValueByPathSetsCorrectValueDataProvider
458 *
459 * Every array splits into:
460 * - Array to set value in
461 * - String path
462 * - Value to set
463 * - Expected result
464 */
465 public function setValueByPathSetsCorrectValueDataProvider() {
466 $testObject = new \StdClass();
467 $testObject->foo = 'foo';
468 $testObject->bar = 'bar';
469 return array(
470 'set integer value: 42' => array(
471 array(
472 'foo' => array(
473 'bar' => array(
474 'baz' => 0
475 )
476 )
477 ),
478 'foo/bar/baz',
479 42,
480 array(
481 'foo' => array(
482 'bar' => array(
483 'baz' => 42
484 )
485 )
486 )
487 ),
488 'set integer value: 0' => array(
489 array(
490 'foo' => array(
491 'bar' => array(
492 'baz' => 42
493 )
494 )
495 ),
496 'foo/bar/baz',
497 0,
498 array(
499 'foo' => array(
500 'bar' => array(
501 'baz' => 0
502 )
503 )
504 )
505 ),
506 'set null value' => array(
507 array(
508 'foo' => array(
509 'bar' => array(
510 'baz' => 42
511 )
512 )
513 ),
514 'foo/bar/baz',
515 NULL,
516 array(
517 'foo' => array(
518 'bar' => array(
519 'baz' => NULL
520 )
521 )
522 )
523 ),
524 'set array value' => array(
525 array(
526 'foo' => array(
527 'bar' => array(
528 'baz' => 42
529 )
530 )
531 ),
532 'foo/bar/baz',
533 array(
534 'foo' => 123
535 ),
536 array(
537 'foo' => array(
538 'bar' => array(
539 'baz' => array(
540 'foo' => 123
541 )
542 )
543 )
544 )
545 ),
546 'set boolean value: FALSE' => array(
547 array(
548 'foo' => array(
549 'bar' => array(
550 'baz' => TRUE
551 )
552 )
553 ),
554 'foo/bar/baz',
555 FALSE,
556 array(
557 'foo' => array(
558 'bar' => array(
559 'baz' => FALSE
560 )
561 )
562 )
563 ),
564 'set boolean value: TRUE' => array(
565 array(
566 'foo' => array(
567 'bar' => array(
568 'baz' => NULL
569 )
570 )
571 ),
572 'foo/bar/baz',
573 TRUE,
574 array(
575 'foo' => array(
576 'bar' => array(
577 'baz' => TRUE
578 )
579 )
580 )
581 ),
582 'set object value' => array(
583 array(
584 'foo' => array(
585 'bar' => array(
586 'baz' => NULL
587 )
588 )
589 ),
590 'foo/bar/baz',
591 $testObject,
592 array(
593 'foo' => array(
594 'bar' => array(
595 'baz' => $testObject
596 )
597 )
598 )
599 ),
600 'multi keys in array' => array(
601 array(
602 'foo' => array(
603 'bar' => array(
604 'baz' => 'value'
605 ),
606 'bar2' => array(
607 'baz' => 'value'
608 )
609 )
610 ),
611 'foo/bar2/baz',
612 'newValue',
613 array(
614 'foo' => array(
615 'bar' => array(
616 'baz' => 'value'
617 ),
618 'bar2' => array(
619 'baz' => 'newValue'
620 )
621 )
622 )
623 )
624 );
625 }
626
627 /**
628 * @test
629 * @dataProvider setValueByPathSetsCorrectValueDataProvider
630 */
631 public function setValueByPathSetsCorrectValue(array $array, $path, $value, $expectedResult) {
632 $this->assertEquals(
633 $expectedResult,
634 ArrayUtility::setValueByPath($array, $path, $value)
635 );
636 }
637
638 /**********************
639 /* Tests concerning removeByPath
640 ***********************/
641
642 /**
643 * @test
644 * @expectedException \RuntimeException
645 */
646 public function removeByPathThrowsExceptionIfPathIsEmpty() {
647 ArrayUtility::removeByPath(array(), '');
648 }
649
650 /**
651 * @test
652 * @expectedException \RuntimeException
653 */
654 public function removeByPathThrowsExceptionIfPathIsNotAString() {
655 ArrayUtility::removeByPath(array(), array('foo'));
656 }
657
658 /**
659 * @test
660 * @expectedException \RuntimeException
661 */
662 public function removeByPathThrowsExceptionWithEmptyPathSegment() {
663 $inputArray = array(
664 'foo' => array(
665 'bar' => 42,
666 ),
667 );
668 ArrayUtility::removeByPath($inputArray, 'foo//bar');
669 }
670
671 /**
672 * @test
673 * @expectedException \RuntimeException
674 */
675 public function removeByPathThrowsExceptionIfPathDoesNotExistInArray() {
676 $inputArray = array(
677 'foo' => array(
678 'bar' => 42,
679 ),
680 );
681 ArrayUtility::removeByPath($inputArray, 'foo/baz');
682 }
683
684 /**
685 * @test
686 */
687 public function removeByPathAcceptsGivenDelimiter() {
688 $inputArray = array(
689 'foo' => array(
690 'toRemove' => 42,
691 'keep' => 23
692 ),
693 );
694 $path = 'foo.toRemove';
695 $expected = array(
696 'foo' => array(
697 'keep' => 23,
698 ),
699 );
700 $this->assertEquals(
701 $expected,
702 ArrayUtility::removeByPath($inputArray, $path, '.')
703 );
704 }
705
706 /**
707 * Data provider for removeByPathRemovesCorrectPath
708 */
709 public function removeByPathRemovesCorrectPathDataProvider() {
710 return array(
711 'single value' => array(
712 array(
713 'foo' => array(
714 'toRemove' => 42,
715 'keep' => 23
716 ),
717 ),
718 'foo/toRemove',
719 array(
720 'foo' => array(
721 'keep' => 23,
722 ),
723 ),
724 ),
725 'whole array' => array(
726 array(
727 'foo' => array(
728 'bar' => 42
729 ),
730 ),
731 'foo',
732 array(),
733 ),
734 'sub array' => array(
735 array(
736 'foo' => array(
737 'keep' => 23,
738 'toRemove' => array(
739 'foo' => 'bar',
740 ),
741 ),
742 ),
743 'foo/toRemove',
744 array(
745 'foo' => array(
746 'keep' => 23,
747 ),
748 ),
749 ),
750 );
751 }
752
753 /**
754 * @test
755 * @dataProvider removeByPathRemovesCorrectPathDataProvider
756 */
757 public function removeByPathRemovesCorrectPath(array $array, $path, $expectedResult) {
758 $this->assertEquals(
759 $expectedResult,
760 ArrayUtility::removeByPath($array, $path)
761 );
762 }
763
764 ///////////////////////
765 // Tests concerning sortByKeyRecursive
766 ///////////////////////
767 /**
768 * @test
769 */
770 public function sortByKeyRecursiveCheckIfSortingIsCorrect() {
771 $unsortedArray = array(
772 'z' => NULL,
773 'a' => NULL,
774 'd' => array(
775 'c' => NULL,
776 'b' => NULL,
777 'd' => NULL,
778 'a' => NULL
779 )
780 );
781 $expectedResult = array(
782 'a' => NULL,
783 'd' => array(
784 'a' => NULL,
785 'b' => NULL,
786 'c' => NULL,
787 'd' => NULL
788 ),
789 'z' => NULL
790 );
791 $this->assertSame($expectedResult, ArrayUtility::sortByKeyRecursive($unsortedArray));
792 }
793
794 ///////////////////////
795 // Tests concerning arrayExport
796 ///////////////////////
797 /**
798 * @test
799 */
800 public function arrayExportReturnsFormattedMultidimensionalArray() {
801 $array = array(
802 'foo' => array(
803 'bar' => 42,
804 'bar2' => array(
805 'baz' => 'val\'ue',
806 'baz2' => TRUE,
807 'baz3' => FALSE,
808 'baz4' => array()
809 )
810 ),
811 'baz' => 23,
812 'foobar' => NULL,
813 'qux' => 0.1,
814 'qux2' => 0.000000001,
815 );
816 $expected =
817 'array(' . LF .
818 TAB . '\'foo\' => array(' . LF .
819 TAB . TAB . '\'bar\' => 42,' . LF .
820 TAB . TAB . '\'bar2\' => array(' . LF .
821 TAB . TAB . TAB . '\'baz\' => \'val\\\'ue\',' . LF .
822 TAB . TAB . TAB . '\'baz2\' => TRUE,' . LF .
823 TAB . TAB . TAB . '\'baz3\' => FALSE,' . LF .
824 TAB . TAB . TAB . '\'baz4\' => array(),' . LF .
825 TAB . TAB . '),' . LF .
826 TAB . '),' . LF .
827 TAB . '\'baz\' => 23,' . LF .
828 TAB . '\'foobar\' => NULL,' . LF .
829 TAB . '\'qux\' => 0.1,' . LF .
830 TAB . '\'qux2\' => 1.0E-9,' . LF .
831 ')';
832 $this->assertSame($expected, ArrayUtility::arrayExport($array));
833 }
834
835 /**
836 * @test
837 * @expectedException \RuntimeException
838 */
839 public function arrayExportThrowsExceptionIfObjectShouldBeExported() {
840 $array = array(
841 'foo' => array(
842 'bar' => new \stdClass()
843 )
844 );
845 ArrayUtility::arrayExport($array);
846 }
847
848 /**
849 * @test
850 */
851 public function arrayExportReturnsNumericArrayKeys() {
852 $array = array(
853 'foo' => 'string key',
854 23 => 'integer key',
855 '42' => 'string key representing integer'
856 );
857 $expected =
858 'array(' . LF .
859 TAB . '\'foo\' => \'string key\',' . LF .
860 TAB . '23 => \'integer key\',' . LF .
861 TAB . '42 => \'string key representing integer\',' . LF .
862 ')';
863 $this->assertSame($expected, ArrayUtility::arrayExport($array));
864 }
865
866 /**
867 * @test
868 */
869 public function arrayExportReturnsNoKeyIndexForConsecutiveCountedArrays() {
870 $array = array(
871 0 => 'zero',
872 1 => 'one',
873 2 => 'two'
874 );
875 $expected =
876 'array(' . LF .
877 TAB . '\'zero\',' . LF .
878 TAB . '\'one\',' . LF .
879 TAB . '\'two\',' . LF .
880 ')';
881 $this->assertSame($expected, ArrayUtility::arrayExport($array));
882 }
883
884 /**
885 * @test
886 */
887 public function arrayExportReturnsKeyIndexForNonConsecutiveCountedArrays() {
888 $array = array(
889 0 => 'zero',
890 1 => 'one',
891 3 => 'three',
892 4 => 'four'
893 );
894 $expected =
895 'array(' . LF .
896 TAB . '0 => \'zero\',' . LF .
897 TAB . '1 => \'one\',' . LF .
898 TAB . '3 => \'three\',' . LF .
899 TAB . '4 => \'four\',' . LF .
900 ')';
901 $this->assertSame($expected, ArrayUtility::arrayExport($array));
902 }
903
904
905 ///////////////////////
906 // Tests concerning flatten
907 ///////////////////////
908
909 /**
910 * @return array
911 */
912 public function flattenCalculatesExpectedResultDataProvider() {
913 return array(
914 'plain array' => array(
915 array(
916 'first' => 1,
917 'second' => 2
918 ),
919 array(
920 'first' => 1,
921 'second' => 2
922 )
923 ),
924 'plain array with faulty dots' => array(
925 array(
926 'first.' => 1,
927 'second.' => 2
928 ),
929 array(
930 'first' => 1,
931 'second' => 2
932 )
933 ),
934 'nested array of 2 levels' => array(
935 array(
936 'first.' => array(
937 'firstSub' => 1
938 ),
939 'second.' => array(
940 'secondSub' => 2
941 )
942 ),
943 array(
944 'first.firstSub' => 1,
945 'second.secondSub' => 2
946 )
947 ),
948 'nested array of 2 levels with faulty dots' => array(
949 array(
950 'first.' => array(
951 'firstSub.' => 1
952 ),
953 'second.' => array(
954 'secondSub.' => 2
955 )
956 ),
957 array(
958 'first.firstSub' => 1,
959 'second.secondSub' => 2
960 )
961 ),
962 'nested array of 3 levels' => array(
963 array(
964 'first.' => array(
965 'firstSub.' => array(
966 'firstSubSub' => 1
967 )
968 ),
969 'second.' => array(
970 'secondSub.' => array(
971 'secondSubSub' => 2
972 )
973 )
974 ),
975 array(
976 'first.firstSub.firstSubSub' => 1,
977 'second.secondSub.secondSubSub' => 2
978 )
979 ),
980 'nested array of 3 levels with faulty dots' => array(
981 array(
982 'first.' => array(
983 'firstSub.' => array(
984 'firstSubSub.' => 1
985 )
986 ),
987 'second.' => array(
988 'secondSub.' => array(
989 'secondSubSub.' => 2
990 )
991 )
992 ),
993 array(
994 'first.firstSub.firstSubSub' => 1,
995 'second.secondSub.secondSubSub' => 2
996 )
997 )
998 );
999 }
1000
1001 /**
1002 * @test
1003 * @param array $array
1004 * @param array $expected
1005 * @dataProvider flattenCalculatesExpectedResultDataProvider
1006 */
1007 public function flattenCalculatesExpectedResult(array $array, array $expected) {
1008 $this->assertEquals($expected, ArrayUtility::flatten($array));
1009 }
1010
1011
1012 ///////////////////////
1013 // Tests concerning intersectRecursive
1014 ///////////////////////
1015
1016 /**
1017 * @return array
1018 */
1019 public function intersectRecursiveCalculatesExpectedResultDataProvider() {
1020 $sameObject = new \stdClass();
1021 return array(
1022 // array($source, $mask, $expected)
1023 'empty array is returned if source is empty array' => array(
1024 array(),
1025 array(
1026 'foo' => 'bar',
1027 ),
1028 array(),
1029 ),
1030 'empty array is returned if mask is empty' => array(
1031 array(
1032 'foo' => 'bar',
1033 ),
1034 array(),
1035 array(),
1036 ),
1037 'key is kept on first level if exists in mask' => array(
1038 array(
1039 'foo' => 42,
1040 ),
1041 array(
1042 'foo' => 42,
1043 ),
1044 array(
1045 'foo' => 42,
1046 ),
1047 ),
1048 'value of key in source is kept if mask has different value' => array(
1049 array(
1050 'foo' => 42,
1051 ),
1052 array(
1053 'foo' => new \stdClass(),
1054 ),
1055 array(
1056 'foo' => 42,
1057 ),
1058 ),
1059 'key is kept on first level if according mask value is NULL' => array(
1060 array(
1061 'foo' => 42,
1062 ),
1063 array(
1064 'foo' => NULL,
1065 ),
1066 array(
1067 'foo' => 42,
1068 ),
1069 ),
1070 'null in source value is kept' => array(
1071 array(
1072 'foo' => NULL,
1073 ),
1074 array(
1075 'foo' => 'bar',
1076 ),
1077 array(
1078 'foo' => NULL,
1079 )
1080 ),
1081 'mask does not add new keys' => array(
1082 array(
1083 'foo' => 42,
1084 ),
1085 array(
1086 'foo' => 23,
1087 'bar' => array(
1088 4711
1089 ),
1090 ),
1091 array(
1092 'foo' => 42,
1093 ),
1094 ),
1095 'mask does not overwrite simple values with arrays' => array(
1096 array(
1097 'foo' => 42,
1098 ),
1099 array(
1100 'foo' => array(
1101 'bar' => 23,
1102 ),
1103 ),
1104 array(
1105 'foo' => 42,
1106 ),
1107 ),
1108 'key is kept on first level if according mask value is array' => array(
1109 array(
1110 'foo' => 42,
1111 ),
1112 array(
1113 'foo' => array(
1114 'bar' => 23
1115 ),
1116 ),
1117 array(
1118 'foo' => 42,
1119 ),
1120 ),
1121 'full array is kept if value is array and mask value is simple type' => array(
1122 array(
1123 'foo' => array(
1124 'bar' => 23
1125 ),
1126 ),
1127 array(
1128 'foo' => 42,
1129 ),
1130 array(
1131 'foo' => array(
1132 'bar' => 23
1133 ),
1134 ),
1135 ),
1136 'key handling is type agnostic' => array(
1137 array(
1138 42 => 'foo',
1139 ),
1140 array(
1141 '42' => 'bar',
1142 ),
1143 array(
1144 42 => 'foo',
1145 ),
1146 ),
1147 'value is same if value is object' => array(
1148 array(
1149 'foo' => $sameObject,
1150 ),
1151 array(
1152 'foo' => 'something',
1153 ),
1154 array(
1155 'foo' => $sameObject,
1156 ),
1157 ),
1158 'mask does not add simple value to result if key does not exist in source' => array(
1159 array(
1160 'foo' => '42',
1161 ),
1162 array(
1163 'foo' => '42',
1164 'bar' => 23
1165 ),
1166 array(
1167 'foo' => '42',
1168 ),
1169 ),
1170 'array of source is kept if value of mask key exists but is no array' => array(
1171 array(
1172 'foo' => '42',
1173 'bar' => array(
1174 'baz' => 23
1175 ),
1176 ),
1177 array(
1178 'foo' => 'value is not significant',
1179 'bar' => NULL,
1180 ),
1181 array(
1182 'foo' => '42',
1183 'bar' => array(
1184 'baz' => 23
1185 ),
1186 ),
1187 ),
1188 'sub arrays are kept if mask has according sub array key and is similar array' => array(
1189 array(
1190 'first1' => 42,
1191 'first2' => array(
1192 'second1' => 23,
1193 'second2' => 4711,
1194 ),
1195 ),
1196 array(
1197 'first1' => 42,
1198 'first2' => array(
1199 'second1' => 'exists but different',
1200 ),
1201 ),
1202 array(
1203 'first1' => 42,
1204 'first2' => array(
1205 'second1' => 23,
1206 ),
1207 ),
1208 ),
1209 );
1210 }
1211
1212 /**
1213 * @test
1214 * @param array $source
1215 * @param array $mask
1216 * @param array $expected
1217 * @dataProvider intersectRecursiveCalculatesExpectedResultDataProvider
1218 */
1219 public function intersectRecursiveCalculatesExpectedResult(array $source, array $mask, array $expected) {
1220 $this->assertSame($expected, ArrayUtility::intersectRecursive($source, $mask));
1221 }
1222
1223
1224 ///////////////////////
1225 // Tests concerning renumberKeysToAvoidLeapsIfKeysAreAllNumeric
1226 ///////////////////////
1227 /**
1228 * @return array
1229 */
1230 public function renumberKeysToAvoidLeapsIfKeysAreAllNumericDataProvider() {
1231 return array(
1232 'empty array is returned if source is empty array' => array(
1233 array(),
1234 array()
1235 ),
1236 'returns self if array is already numerically keyed' => array(
1237 array(1,2,3),
1238 array(1,2,3)
1239 ),
1240 'returns correctly if keys are numeric, but contains a leap' => array(
1241 array(0 => 'One', 1 => 'Two', 3 => 'Three'),
1242 array(0 => 'One', 1 => 'Two', 2 => 'Three'),
1243 ),
1244 'returns correctly even though keys are strings but still numeric' => array(
1245 array('0' => 'One', '1' => 'Two', '3' => 'Three'),
1246 array(0 => 'One', 1 => 'Two', 2 => 'Three'),
1247 ),
1248 'returns correctly if just a single keys is not numeric' => array(
1249 array(0 => 'Zero', '1' => 'One', 'Two' => 'Two'),
1250 array(0 => 'Zero', '1' => 'One', 'Two' => 'Two'),
1251 ),
1252 'return self with nested numerically keyed array' => array(
1253 array(
1254 'One',
1255 'Two',
1256 'Three',
1257 array(
1258 'sub.One',
1259 'sub.Two',
1260 )
1261 ),
1262 array(
1263 'One',
1264 'Two',
1265 'Three',
1266 array(
1267 'sub.One',
1268 'sub.Two',
1269 )
1270 )
1271 ),
1272 'returns correctly with nested numerically keyed array with leaps' => array(
1273 array(
1274 'One',
1275 'Two',
1276 'Three',
1277 array(
1278 0 => 'sub.One',
1279 2 => 'sub.Two',
1280 )
1281 ),
1282 array(
1283 'One',
1284 'Two',
1285 'Three',
1286 array(
1287 'sub.One',
1288 'sub.Two',
1289 )
1290 )
1291 ),
1292 'returns correctly with nested string-keyed array' => array(
1293 array(
1294 'One',
1295 'Two',
1296 'Three',
1297 array(
1298 'one' => 'sub.One',
1299 'two' => 'sub.Two',
1300 )
1301 ),
1302 array(
1303 'One',
1304 'Two',
1305 'Three',
1306 array(
1307 'one' => 'sub.One',
1308 'two' => 'sub.Two',
1309 )
1310 )
1311 ),
1312 'returns correctly with deeply nested arrays' => array(
1313 array(
1314 'One',
1315 'Two',
1316 array(
1317 'one' => 1,
1318 'two' => 2,
1319 'three' => array(
1320 2 => 'SubSubOne',
1321 5 => 'SubSubTwo',
1322 9 => array(0,1,2),
1323 array()
1324 )
1325 )
1326 ),
1327 array(
1328 'One',
1329 'Two',
1330 array(
1331 'one' => 1,
1332 'two' => 2,
1333 'three' => array(
1334 'SubSubOne',
1335 'SubSubTwo',
1336 array(0,1,2),
1337 array()
1338 )
1339 )
1340 )
1341 )
1342 );
1343 }
1344
1345 /**
1346 * @test
1347 * @param array $inputArray
1348 * @param array $expected
1349 * @dataProvider renumberKeysToAvoidLeapsIfKeysAreAllNumericDataProvider
1350 */
1351 public function renumberKeysToAvoidLeapsIfKeysAreAllNumeric(array $inputArray, array $expected) {
1352 $this->assertEquals($expected, ArrayUtility::renumberKeysToAvoidLeapsIfKeysAreAllNumeric($inputArray));
1353 }
1354
1355 }
1356
1357 ?>