[TASK] Removes eval() in ArrayUtilityTest
[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 * 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\UnitTestCase;
18 use TYPO3\CMS\Core\Utility\ArrayUtility;
19
20 /**
21 * Test case
22 *
23 * @author Susanne Moog <typo3@susanne-moog.de>
24 * @author Christian Kuhn <lolli@schwarzbu.ch>
25 */
26 class ArrayUtilityTest extends UnitTestCase {
27
28 ///////////////////////
29 // Tests concerning filterByValueRecursive
30 ///////////////////////
31 /**
32 * Data provider for filterByValueRecursiveCorrectlyFiltersArray
33 *
34 * Every array splits into:
35 * - String value to search for
36 * - Input array
37 * - Expected result array
38 */
39 public function filterByValueRecursive() {
40 return array(
41 'empty search array' => array(
42 'banana',
43 array(),
44 array()
45 ),
46 'empty string as needle' => array(
47 '',
48 array(
49 '',
50 'apple'
51 ),
52 array(
53 ''
54 )
55 ),
56 'flat array searching for string' => array(
57 'banana',
58 array(
59 'apple',
60 'banana'
61 ),
62 array(
63 1 => 'banana'
64 )
65 ),
66 'flat array searching for string with two matches' => array(
67 'banana',
68 array(
69 'foo' => 'apple',
70 'firstbanana' => 'banana',
71 'secondbanana' => 'banana'
72 ),
73 array(
74 'firstbanana' => 'banana',
75 'secondbanana' => 'banana'
76 )
77 ),
78 'multi dimensional array searching for string with multiple matches' => array(
79 'banana',
80 array(
81 'foo' => 'apple',
82 'firstbanana' => 'banana',
83 'grape' => array(
84 'foo2' => 'apple2',
85 'secondbanana' => 'banana',
86 'foo3' => array()
87 ),
88 'bar' => 'orange'
89 ),
90 array(
91 'firstbanana' => 'banana',
92 'grape' => array(
93 'secondbanana' => 'banana'
94 )
95 )
96 ),
97 'multi dimensional array searching for integer with multiple matches' => array(
98 42,
99 array(
100 'foo' => 23,
101 'bar' => 42,
102 array(
103 'foo' => 23,
104 'bar' => 42
105 )
106 ),
107 array(
108 'bar' => 42,
109 array(
110 'bar' => 42
111 )
112 )
113 ),
114 'flat array searching for boolean TRUE' => array(
115 TRUE,
116 array(
117 23 => FALSE,
118 42 => TRUE
119 ),
120 array(
121 42 => TRUE
122 )
123 ),
124 'multi dimensional array searching for boolean FALSE' => array(
125 FALSE,
126 array(
127 23 => FALSE,
128 42 => TRUE,
129 'foo' => array(
130 23 => FALSE,
131 42 => TRUE
132 )
133 ),
134 array(
135 23 => FALSE,
136 'foo' => array(
137 23 => FALSE
138 )
139 )
140 ),
141 'flat array searching for array' => array(
142 array(
143 'foo' => 'bar'
144 ),
145 array(
146 'foo' => 'bar',
147 'foobar' => array(
148 'foo' => 'bar'
149 )
150 ),
151 array(
152 'foobar' => array(
153 'foo' => 'bar'
154 )
155 )
156 )
157 );
158 }
159
160 /**
161 * @test
162 * @dataProvider filterByValueRecursive
163 * @param array $needle
164 * @param array $haystack
165 * @param array $expectedResult
166 */
167 public function filterByValueRecursiveCorrectlyFiltersArray($needle, $haystack, $expectedResult) {
168 $this->assertEquals(
169 $expectedResult,
170 ArrayUtility::filterByValueRecursive($needle, $haystack)
171 );
172 }
173
174 /**
175 * @test
176 */
177 public function filterByValueRecursiveMatchesReferencesToSameObject() {
178 $instance = new \stdClass();
179 $this->assertEquals(
180 array($instance),
181 ArrayUtility::filterByValueRecursive($instance, array($instance))
182 );
183 }
184
185 /**
186 * @test
187 */
188 public function filterByValueRecursiveDoesNotMatchDifferentInstancesOfSameClass() {
189 $this->assertEquals(
190 array(),
191 ArrayUtility::filterByValueRecursive(new \stdClass(), array(new \stdClass()))
192 );
193 }
194
195 ///////////////////////
196 // Tests concerning isValidPath
197 ///////////////////////
198 /**
199 * Mock the class under test, isValidPath() (method under test), calls
200 * static getValuePath() internally, which is mocked here to return a specific
201 * result. This works because of 'static' keyword' instead of 'self'
202 * for getValueByPath() call, using late static binding in PHP 5.3
203 *
204 * @test
205 */
206 public function isValidPathReturnsTrueIfPathExists() {
207 $this->assertTrue(ArrayUtility::isValidPath(array('foo' => 'bar'), 'foo'));
208 }
209
210 /**
211 * @test
212 */
213 public function isValidPathReturnsFalseIfPathDoesNotExist() {
214 $this->assertFalse(ArrayUtility::isValidPath(array('foo' => 'bar'), 'bar'));
215 }
216
217 ///////////////////////
218 // Tests concerning getValueByPath
219 ///////////////////////
220 /**
221 * @test
222 * @expectedException \RuntimeException
223 */
224 public function getValueByPathThrowsExceptionIfPathIsEmpty() {
225 ArrayUtility::getValueByPath(array(), '');
226 }
227
228 /**
229 * Data provider for getValueByPathThrowsExceptionIfPathNotExists
230 * Every array splits into:
231 * - Array to get value from
232 * - String path
233 * - Expected result
234 * @return array
235 */
236 public function getValueByPathInvalidPathDataProvider() {
237 return array(
238 'not existing path 1' => array(
239 array(
240 'foo' => array()
241 ),
242 'foo/bar/baz',
243 FALSE
244 ),
245 'not existing path 2' => array(
246 array(
247 'foo' => array(
248 'baz' => 42
249 ),
250 'bar' => array()
251 ),
252 'foo/bar/baz',
253 FALSE
254 ),
255 // Negative test: This could be improved and the test moved to
256 // the valid data provider if the method supports this
257 'doubletick encapsulated quoted doubletick does not work' => array(
258 array(
259 '"foo"bar"' => array(
260 'baz' => 42
261 ),
262 'bar' => array()
263 ),
264 '"foo\\"bar"/baz',
265 42
266 ),
267 // Negative test: Method could be improved here
268 'path with doubletick does not work' => array(
269 array(
270 'fo"o' => array(
271 'bar' => 42
272 )
273 ),
274 'fo"o/foobar',
275 42
276 )
277 );
278 }
279
280 /**
281 * @test
282 * @dataProvider getValueByPathInvalidPathDataProvider
283 * @expectedException \RuntimeException
284 * @param array $array
285 * @param string $path
286 */
287 public function getValueByPathThrowsExceptionIfPathNotExists(array $array, $path) {
288 ArrayUtility::getValueByPath($array, $path);
289 }
290
291 /**
292 * Data provider for getValueByPathReturnsCorrectValue
293 * Every array splits into:
294 * - Array to get value from
295 * - String path
296 * - Expected result
297 */
298 public function getValueByPathValidDataProvider() {
299 $testObject = new \StdClass();
300 $testObject->foo = 'foo';
301 $testObject->bar = 'bar';
302 return array(
303 'integer in multi level array' => array(
304 array(
305 'foo' => array(
306 'bar' => array(
307 'baz' => 42
308 ),
309 'bar2' => array()
310 )
311 ),
312 'foo/bar/baz',
313 42
314 ),
315 'zero integer in multi level array' => array(
316 array(
317 'foo' => array(
318 'bar' => array(
319 'baz' => 0
320 )
321 )
322 ),
323 'foo/bar/baz',
324 0
325 ),
326 'NULL value in multi level array' => array(
327 array(
328 'foo' => array(
329 'baz' => NULL
330 )
331 ),
332 'foo/baz',
333 NULL
334 ),
335 'get string value' => array(
336 array(
337 'foo' => array(
338 'baz' => 'this is a test string'
339 )
340 ),
341 'foo/baz',
342 'this is a test string'
343 ),
344 'get boolean value: FALSE' => array(
345 array(
346 'foo' => array(
347 'baz' => FALSE
348 )
349 ),
350 'foo/baz',
351 FALSE
352 ),
353 'get boolean value: TRUE' => array(
354 array(
355 'foo' => array(
356 'baz' => TRUE
357 )
358 ),
359 'foo/baz',
360 TRUE
361 ),
362 'get object value' => array(
363 array(
364 'foo' => array(
365 'baz' => $testObject
366 )
367 ),
368 'foo/baz',
369 $testObject
370 ),
371 'enclosed path' => array(
372 array(
373 'foo/bar' => array(
374 'foobar' => 42
375 )
376 ),
377 '"foo/bar"/foobar',
378 42
379 )
380 );
381 }
382
383 /**
384 * @test
385 * @dataProvider getValueByPathValidDataProvider
386 * @param array $array
387 * @param string $path
388 * @param mixed $expectedResult
389 */
390 public function getValueByPathGetsCorrectValue(array $array, $path, $expectedResult) {
391 $this->assertEquals($expectedResult, ArrayUtility::getValueByPath($array, $path));
392 }
393
394 /**
395 * @test
396 */
397 public function getValueByPathAcceptsDifferentDelimiter() {
398 $input = array(
399 'foo' => array(
400 'bar' => array(
401 'baz' => 42
402 ),
403 'bar2' => array()
404 )
405 );
406 $searchPath = 'foo%bar%baz';
407 $expected = 42;
408 $delimiter = '%';
409 $this->assertEquals(
410 $expected,
411 ArrayUtility::getValueByPath($input, $searchPath, $delimiter)
412 );
413 }
414
415 ///////////////////////
416 // Tests concerning setValueByPath
417 ///////////////////////
418 /**
419 * @test
420 * @expectedException \RuntimeException
421 */
422 public function setValueByPathThrowsExceptionIfPathIsEmpty() {
423 ArrayUtility::setValueByPath(array(), '', NULL);
424 }
425
426 /**
427 * @test
428 * @expectedException \RuntimeException
429 */
430 public function setValueByPathThrowsExceptionIfPathIsNotAString() {
431 ArrayUtility::setValueByPath(array(), array('foo'), NULL);
432 }
433
434 /**
435 * Data provider for setValueByPathSetsCorrectValueDataProvider
436 *
437 * Every array splits into:
438 * - Array to set value in
439 * - String path
440 * - Value to set
441 * - Expected result
442 */
443 public function setValueByPathSetsCorrectValueDataProvider() {
444 $testObject = new \StdClass();
445 $testObject->foo = 'foo';
446 $testObject->bar = 'bar';
447 return array(
448 'set integer value: 42' => array(
449 array(
450 'foo' => array(
451 'bar' => array(
452 'baz' => 0
453 )
454 )
455 ),
456 'foo/bar/baz',
457 42,
458 array(
459 'foo' => array(
460 'bar' => array(
461 'baz' => 42
462 )
463 )
464 )
465 ),
466 'set integer value: 0' => array(
467 array(
468 'foo' => array(
469 'bar' => array(
470 'baz' => 42
471 )
472 )
473 ),
474 'foo/bar/baz',
475 0,
476 array(
477 'foo' => array(
478 'bar' => array(
479 'baz' => 0
480 )
481 )
482 )
483 ),
484 'set null value' => array(
485 array(
486 'foo' => array(
487 'bar' => array(
488 'baz' => 42
489 )
490 )
491 ),
492 'foo/bar/baz',
493 NULL,
494 array(
495 'foo' => array(
496 'bar' => array(
497 'baz' => NULL
498 )
499 )
500 )
501 ),
502 'set array value' => array(
503 array(
504 'foo' => array(
505 'bar' => array(
506 'baz' => 42
507 )
508 )
509 ),
510 'foo/bar/baz',
511 array(
512 'foo' => 123
513 ),
514 array(
515 'foo' => array(
516 'bar' => array(
517 'baz' => array(
518 'foo' => 123
519 )
520 )
521 )
522 )
523 ),
524 'set boolean value: FALSE' => array(
525 array(
526 'foo' => array(
527 'bar' => array(
528 'baz' => TRUE
529 )
530 )
531 ),
532 'foo/bar/baz',
533 FALSE,
534 array(
535 'foo' => array(
536 'bar' => array(
537 'baz' => FALSE
538 )
539 )
540 )
541 ),
542 'set boolean value: TRUE' => array(
543 array(
544 'foo' => array(
545 'bar' => array(
546 'baz' => NULL
547 )
548 )
549 ),
550 'foo/bar/baz',
551 TRUE,
552 array(
553 'foo' => array(
554 'bar' => array(
555 'baz' => TRUE
556 )
557 )
558 )
559 ),
560 'set object value' => array(
561 array(
562 'foo' => array(
563 'bar' => array(
564 'baz' => NULL
565 )
566 )
567 ),
568 'foo/bar/baz',
569 $testObject,
570 array(
571 'foo' => array(
572 'bar' => array(
573 'baz' => $testObject
574 )
575 )
576 )
577 ),
578 'multi keys in array' => array(
579 array(
580 'foo' => array(
581 'bar' => array(
582 'baz' => 'value'
583 ),
584 'bar2' => array(
585 'baz' => 'value'
586 )
587 )
588 ),
589 'foo/bar2/baz',
590 'newValue',
591 array(
592 'foo' => array(
593 'bar' => array(
594 'baz' => 'value'
595 ),
596 'bar2' => array(
597 'baz' => 'newValue'
598 )
599 )
600 )
601 )
602 );
603 }
604
605 /**
606 * @test
607 * @dataProvider setValueByPathSetsCorrectValueDataProvider
608 * @param array $array
609 * @param string $path
610 * @param string $value
611 * @param array $expectedResult
612 */
613 public function setValueByPathSetsCorrectValue(array $array, $path, $value, $expectedResult) {
614 $this->assertEquals(
615 $expectedResult,
616 ArrayUtility::setValueByPath($array, $path, $value)
617 );
618 }
619
620 /**********************
621 /* Tests concerning removeByPath
622 ***********************/
623
624 /**
625 * @test
626 * @expectedException \RuntimeException
627 */
628 public function removeByPathThrowsExceptionIfPathIsEmpty() {
629 ArrayUtility::removeByPath(array(), '');
630 }
631
632 /**
633 * @test
634 * @expectedException \RuntimeException
635 */
636 public function removeByPathThrowsExceptionIfPathIsNotAString() {
637 ArrayUtility::removeByPath(array(), array('foo'));
638 }
639
640 /**
641 * @test
642 * @expectedException \RuntimeException
643 */
644 public function removeByPathThrowsExceptionWithEmptyPathSegment() {
645 $inputArray = array(
646 'foo' => array(
647 'bar' => 42,
648 ),
649 );
650 ArrayUtility::removeByPath($inputArray, 'foo//bar');
651 }
652
653 /**
654 * @test
655 * @expectedException \RuntimeException
656 */
657 public function removeByPathThrowsExceptionIfPathDoesNotExistInArray() {
658 $inputArray = array(
659 'foo' => array(
660 'bar' => 42,
661 ),
662 );
663 ArrayUtility::removeByPath($inputArray, 'foo/baz');
664 }
665
666 /**
667 * @test
668 */
669 public function removeByPathAcceptsGivenDelimiter() {
670 $inputArray = array(
671 'foo' => array(
672 'toRemove' => 42,
673 'keep' => 23
674 ),
675 );
676 $path = 'foo.toRemove';
677 $expected = array(
678 'foo' => array(
679 'keep' => 23,
680 ),
681 );
682 $this->assertEquals(
683 $expected,
684 ArrayUtility::removeByPath($inputArray, $path, '.')
685 );
686 }
687
688 /**
689 * Data provider for removeByPathRemovesCorrectPath
690 */
691 public function removeByPathRemovesCorrectPathDataProvider() {
692 return array(
693 'single value' => array(
694 array(
695 'foo' => array(
696 'toRemove' => 42,
697 'keep' => 23
698 ),
699 ),
700 'foo/toRemove',
701 array(
702 'foo' => array(
703 'keep' => 23,
704 ),
705 ),
706 ),
707 'whole array' => array(
708 array(
709 'foo' => array(
710 'bar' => 42
711 ),
712 ),
713 'foo',
714 array(),
715 ),
716 'sub array' => array(
717 array(
718 'foo' => array(
719 'keep' => 23,
720 'toRemove' => array(
721 'foo' => 'bar',
722 ),
723 ),
724 ),
725 'foo/toRemove',
726 array(
727 'foo' => array(
728 'keep' => 23,
729 ),
730 ),
731 ),
732 );
733 }
734
735 /**
736 * @test
737 * @dataProvider removeByPathRemovesCorrectPathDataProvider
738 * @param array $array
739 * @param string $path
740 * @param array $expectedResult
741 */
742 public function removeByPathRemovesCorrectPath(array $array, $path, $expectedResult) {
743 $this->assertEquals(
744 $expectedResult,
745 ArrayUtility::removeByPath($array, $path)
746 );
747 }
748
749 ///////////////////////
750 // Tests concerning sortByKeyRecursive
751 ///////////////////////
752 /**
753 * @test
754 */
755 public function sortByKeyRecursiveCheckIfSortingIsCorrect() {
756 $unsortedArray = array(
757 'z' => NULL,
758 'a' => NULL,
759 'd' => array(
760 'c' => NULL,
761 'b' => NULL,
762 'd' => NULL,
763 'a' => NULL
764 )
765 );
766 $expectedResult = array(
767 'a' => NULL,
768 'd' => array(
769 'a' => NULL,
770 'b' => NULL,
771 'c' => NULL,
772 'd' => NULL
773 ),
774 'z' => NULL
775 );
776 $this->assertSame($expectedResult, ArrayUtility::sortByKeyRecursive($unsortedArray));
777 }
778
779 ///////////////////////
780 // Tests concerning sortArraysByKey
781 ///////////////////////
782 /**
783 * Data provider for sortArraysByKeyCheckIfSortingIsCorrect
784 */
785 public function sortArraysByKeyCheckIfSortingIsCorrectDataProvider() {
786 return array(
787 'assoc array index' => array(
788 array(
789 '22' => array(
790 'uid' => '22',
791 'title' => 'c',
792 'dummy' => 2
793 ),
794 '24' => array(
795 'uid' => '24',
796 'title' => 'a',
797 'dummy' => 3
798 ),
799 '23' => array(
800 'uid' => '23',
801 'title' => 'b',
802 'dummy' => 4
803 ),
804 ),
805 'title',
806 TRUE,
807 array(
808 '24' => array(
809 'uid' => '24',
810 'title' => 'a',
811 'dummy' => 3
812 ),
813 '23' => array(
814 'uid' => '23',
815 'title' => 'b',
816 'dummy' => 4
817 ),
818 '22' => array(
819 'uid' => '22',
820 'title' => 'c',
821 'dummy' => 2
822 ),
823 ),
824 ),
825 'numeric array index' => array(
826 array(
827 22 => array(
828 'uid' => '22',
829 'title' => 'c',
830 'dummy' => 2
831 ),
832 24 => array(
833 'uid' => '24',
834 'title' => 'a',
835 'dummy' => 3
836 ),
837 23 => array(
838 'uid' => '23',
839 'title' => 'b',
840 'dummy' => 4
841 ),
842 ),
843 'title',
844 TRUE,
845 array(
846 24 => array(
847 'uid' => '24',
848 'title' => 'a',
849 'dummy' => 3
850 ),
851 23 => array(
852 'uid' => '23',
853 'title' => 'b',
854 'dummy' => 4
855 ),
856 22 => array(
857 'uid' => '22',
858 'title' => 'c',
859 'dummy' => 2
860 ),
861 ),
862 ),
863 'numeric array index DESC' => array(
864 array(
865 23 => array(
866 'uid' => '23',
867 'title' => 'b',
868 'dummy' => 4
869 ),
870 22 => array(
871 'uid' => '22',
872 'title' => 'c',
873 'dummy' => 2
874 ),
875 24 => array(
876 'uid' => '24',
877 'title' => 'a',
878 'dummy' => 3
879 ),
880 ),
881 'title',
882 FALSE,
883 array(
884 22 => array(
885 'uid' => '22',
886 'title' => 'c',
887 'dummy' => 2
888 ),
889 23 => array(
890 'uid' => '23',
891 'title' => 'b',
892 'dummy' => 4
893 ),
894 24 => array(
895 'uid' => '24',
896 'title' => 'a',
897 'dummy' => 3
898 ),
899 ),
900 ),
901 );
902 }
903
904 /**
905 * @test
906 * @dataProvider sortArraysByKeyCheckIfSortingIsCorrectDataProvider
907 * @param array $array
908 * @param string $key
909 * @param bool $ascending
910 * @param array $expectedResult
911 */
912 public function sortArraysByKeyCheckIfSortingIsCorrect(array $array, $key, $ascending, $expectedResult) {
913 $sortedArray = ArrayUtility::sortArraysByKey($array, $key, $ascending);
914 $this->assertSame($expectedResult, $sortedArray);
915 }
916
917 /**
918 * @test
919 * @expectedException \RuntimeException
920 */
921 public function sortArraysByKeyThrowsExceptionForNonExistingKey() {
922 ArrayUtility::sortArraysByKey(array(array('a'), array('a')), 'dummy');
923 }
924
925 ///////////////////////
926 // Tests concerning arrayExport
927 ///////////////////////
928 /**
929 * @test
930 */
931 public function arrayExportReturnsFormattedMultidimensionalArray() {
932 $array = array(
933 'foo' => array(
934 'bar' => 42,
935 'bar2' => array(
936 'baz' => 'val\'ue',
937 'baz2' => TRUE,
938 'baz3' => FALSE,
939 'baz4' => array()
940 )
941 ),
942 'baz' => 23,
943 'foobar' => NULL,
944 'qux' => 0.1,
945 'qux2' => 0.000000001,
946 );
947 $expected =
948 'array(' . LF .
949 TAB . '\'foo\' => array(' . LF .
950 TAB . TAB . '\'bar\' => 42,' . LF .
951 TAB . TAB . '\'bar2\' => array(' . LF .
952 TAB . TAB . TAB . '\'baz\' => \'val\\\'ue\',' . LF .
953 TAB . TAB . TAB . '\'baz2\' => TRUE,' . LF .
954 TAB . TAB . TAB . '\'baz3\' => FALSE,' . LF .
955 TAB . TAB . TAB . '\'baz4\' => array(),' . LF .
956 TAB . TAB . '),' . LF .
957 TAB . '),' . LF .
958 TAB . '\'baz\' => 23,' . LF .
959 TAB . '\'foobar\' => NULL,' . LF .
960 TAB . '\'qux\' => 0.1,' . LF .
961 TAB . '\'qux2\' => 1.0E-9,' . LF .
962 ')';
963 $this->assertSame($expected, ArrayUtility::arrayExport($array));
964 }
965
966 /**
967 * @test
968 * @expectedException \RuntimeException
969 */
970 public function arrayExportThrowsExceptionIfObjectShouldBeExported() {
971 $array = array(
972 'foo' => array(
973 'bar' => new \stdClass()
974 )
975 );
976 ArrayUtility::arrayExport($array);
977 }
978
979 /**
980 * @test
981 */
982 public function arrayExportReturnsNumericArrayKeys() {
983 $array = array(
984 'foo' => 'string key',
985 23 => 'integer key',
986 '42' => 'string key representing integer'
987 );
988 $expected =
989 'array(' . LF .
990 TAB . '\'foo\' => \'string key\',' . LF .
991 TAB . '23 => \'integer key\',' . LF .
992 TAB . '42 => \'string key representing integer\',' . LF .
993 ')';
994 $this->assertSame($expected, ArrayUtility::arrayExport($array));
995 }
996
997 /**
998 * @test
999 */
1000 public function arrayExportReturnsNoKeyIndexForConsecutiveCountedArrays() {
1001 $array = array(
1002 0 => 'zero',
1003 1 => 'one',
1004 2 => 'two'
1005 );
1006 $expected =
1007 'array(' . LF .
1008 TAB . '\'zero\',' . LF .
1009 TAB . '\'one\',' . LF .
1010 TAB . '\'two\',' . LF .
1011 ')';
1012 $this->assertSame($expected, ArrayUtility::arrayExport($array));
1013 }
1014
1015 /**
1016 * @test
1017 */
1018 public function arrayExportReturnsKeyIndexForNonConsecutiveCountedArrays() {
1019 $array = array(
1020 0 => 'zero',
1021 1 => 'one',
1022 3 => 'three',
1023 4 => 'four'
1024 );
1025 $expected =
1026 'array(' . LF .
1027 TAB . '0 => \'zero\',' . LF .
1028 TAB . '1 => \'one\',' . LF .
1029 TAB . '3 => \'three\',' . LF .
1030 TAB . '4 => \'four\',' . LF .
1031 ')';
1032 $this->assertSame($expected, ArrayUtility::arrayExport($array));
1033 }
1034
1035
1036 ///////////////////////
1037 // Tests concerning flatten
1038 ///////////////////////
1039
1040 /**
1041 * @return array
1042 */
1043 public function flattenCalculatesExpectedResultDataProvider() {
1044 return array(
1045 'plain array' => array(
1046 array(
1047 'first' => 1,
1048 'second' => 2
1049 ),
1050 array(
1051 'first' => 1,
1052 'second' => 2
1053 )
1054 ),
1055 'plain array with faulty dots' => array(
1056 array(
1057 'first.' => 1,
1058 'second.' => 2
1059 ),
1060 array(
1061 'first' => 1,
1062 'second' => 2
1063 )
1064 ),
1065 'nested array of 2 levels' => array(
1066 array(
1067 'first.' => array(
1068 'firstSub' => 1
1069 ),
1070 'second.' => array(
1071 'secondSub' => 2
1072 )
1073 ),
1074 array(
1075 'first.firstSub' => 1,
1076 'second.secondSub' => 2
1077 )
1078 ),
1079 'nested array of 2 levels with faulty dots' => array(
1080 array(
1081 'first.' => array(
1082 'firstSub.' => 1
1083 ),
1084 'second.' => array(
1085 'secondSub.' => 2
1086 )
1087 ),
1088 array(
1089 'first.firstSub' => 1,
1090 'second.secondSub' => 2
1091 )
1092 ),
1093 'nested array of 3 levels' => array(
1094 array(
1095 'first.' => array(
1096 'firstSub.' => array(
1097 'firstSubSub' => 1
1098 )
1099 ),
1100 'second.' => array(
1101 'secondSub.' => array(
1102 'secondSubSub' => 2
1103 )
1104 )
1105 ),
1106 array(
1107 'first.firstSub.firstSubSub' => 1,
1108 'second.secondSub.secondSubSub' => 2
1109 )
1110 ),
1111 'nested array of 3 levels with faulty dots' => array(
1112 array(
1113 'first.' => array(
1114 'firstSub.' => array(
1115 'firstSubSub.' => 1
1116 )
1117 ),
1118 'second.' => array(
1119 'secondSub.' => array(
1120 'secondSubSub.' => 2
1121 )
1122 )
1123 ),
1124 array(
1125 'first.firstSub.firstSubSub' => 1,
1126 'second.secondSub.secondSubSub' => 2
1127 )
1128 )
1129 );
1130 }
1131
1132 /**
1133 * @test
1134 * @param array $array
1135 * @param array $expected
1136 * @dataProvider flattenCalculatesExpectedResultDataProvider
1137 */
1138 public function flattenCalculatesExpectedResult(array $array, array $expected) {
1139 $this->assertEquals($expected, ArrayUtility::flatten($array));
1140 }
1141
1142
1143 ///////////////////////
1144 // Tests concerning intersectRecursive
1145 ///////////////////////
1146
1147 /**
1148 * @return array
1149 */
1150 public function intersectRecursiveCalculatesExpectedResultDataProvider() {
1151 $sameObject = new \stdClass();
1152 return array(
1153 // array($source, $mask, $expected)
1154 'empty array is returned if source is empty array' => array(
1155 array(),
1156 array(
1157 'foo' => 'bar',
1158 ),
1159 array(),
1160 ),
1161 'empty array is returned if mask is empty' => array(
1162 array(
1163 'foo' => 'bar',
1164 ),
1165 array(),
1166 array(),
1167 ),
1168 'key is kept on first level if exists in mask' => array(
1169 array(
1170 'foo' => 42,
1171 ),
1172 array(
1173 'foo' => 42,
1174 ),
1175 array(
1176 'foo' => 42,
1177 ),
1178 ),
1179 'value of key in source is kept if mask has different value' => array(
1180 array(
1181 'foo' => 42,
1182 ),
1183 array(
1184 'foo' => new \stdClass(),
1185 ),
1186 array(
1187 'foo' => 42,
1188 ),
1189 ),
1190 'key is kept on first level if according mask value is NULL' => array(
1191 array(
1192 'foo' => 42,
1193 ),
1194 array(
1195 'foo' => NULL,
1196 ),
1197 array(
1198 'foo' => 42,
1199 ),
1200 ),
1201 'null in source value is kept' => array(
1202 array(
1203 'foo' => NULL,
1204 ),
1205 array(
1206 'foo' => 'bar',
1207 ),
1208 array(
1209 'foo' => NULL,
1210 )
1211 ),
1212 'mask does not add new keys' => array(
1213 array(
1214 'foo' => 42,
1215 ),
1216 array(
1217 'foo' => 23,
1218 'bar' => array(
1219 4711
1220 ),
1221 ),
1222 array(
1223 'foo' => 42,
1224 ),
1225 ),
1226 'mask does not overwrite simple values with arrays' => array(
1227 array(
1228 'foo' => 42,
1229 ),
1230 array(
1231 'foo' => array(
1232 'bar' => 23,
1233 ),
1234 ),
1235 array(
1236 'foo' => 42,
1237 ),
1238 ),
1239 'key is kept on first level if according mask value is array' => array(
1240 array(
1241 'foo' => 42,
1242 ),
1243 array(
1244 'foo' => array(
1245 'bar' => 23
1246 ),
1247 ),
1248 array(
1249 'foo' => 42,
1250 ),
1251 ),
1252 'full array is kept if value is array and mask value is simple type' => array(
1253 array(
1254 'foo' => array(
1255 'bar' => 23
1256 ),
1257 ),
1258 array(
1259 'foo' => 42,
1260 ),
1261 array(
1262 'foo' => array(
1263 'bar' => 23
1264 ),
1265 ),
1266 ),
1267 'key handling is type agnostic' => array(
1268 array(
1269 42 => 'foo',
1270 ),
1271 array(
1272 '42' => 'bar',
1273 ),
1274 array(
1275 42 => 'foo',
1276 ),
1277 ),
1278 'value is same if value is object' => array(
1279 array(
1280 'foo' => $sameObject,
1281 ),
1282 array(
1283 'foo' => 'something',
1284 ),
1285 array(
1286 'foo' => $sameObject,
1287 ),
1288 ),
1289 'mask does not add simple value to result if key does not exist in source' => array(
1290 array(
1291 'foo' => '42',
1292 ),
1293 array(
1294 'foo' => '42',
1295 'bar' => 23
1296 ),
1297 array(
1298 'foo' => '42',
1299 ),
1300 ),
1301 'array of source is kept if value of mask key exists but is no array' => array(
1302 array(
1303 'foo' => '42',
1304 'bar' => array(
1305 'baz' => 23
1306 ),
1307 ),
1308 array(
1309 'foo' => 'value is not significant',
1310 'bar' => NULL,
1311 ),
1312 array(
1313 'foo' => '42',
1314 'bar' => array(
1315 'baz' => 23
1316 ),
1317 ),
1318 ),
1319 'sub arrays are kept if mask has according sub array key and is similar array' => array(
1320 array(
1321 'first1' => 42,
1322 'first2' => array(
1323 'second1' => 23,
1324 'second2' => 4711,
1325 ),
1326 ),
1327 array(
1328 'first1' => 42,
1329 'first2' => array(
1330 'second1' => 'exists but different',
1331 ),
1332 ),
1333 array(
1334 'first1' => 42,
1335 'first2' => array(
1336 'second1' => 23,
1337 ),
1338 ),
1339 ),
1340 );
1341 }
1342
1343 /**
1344 * @test
1345 * @param array $source
1346 * @param array $mask
1347 * @param array $expected
1348 * @dataProvider intersectRecursiveCalculatesExpectedResultDataProvider
1349 */
1350 public function intersectRecursiveCalculatesExpectedResult(array $source, array $mask, array $expected) {
1351 $this->assertSame($expected, ArrayUtility::intersectRecursive($source, $mask));
1352 }
1353
1354
1355 ///////////////////////
1356 // Tests concerning renumberKeysToAvoidLeapsIfKeysAreAllNumeric
1357 ///////////////////////
1358 /**
1359 * @return array
1360 */
1361 public function renumberKeysToAvoidLeapsIfKeysAreAllNumericDataProvider() {
1362 return array(
1363 'empty array is returned if source is empty array' => array(
1364 array(),
1365 array()
1366 ),
1367 'returns self if array is already numerically keyed' => array(
1368 array(1,2,3),
1369 array(1,2,3)
1370 ),
1371 'returns correctly if keys are numeric, but contains a leap' => array(
1372 array(0 => 'One', 1 => 'Two', 3 => 'Three'),
1373 array(0 => 'One', 1 => 'Two', 2 => 'Three'),
1374 ),
1375 'returns correctly even though keys are strings but still numeric' => array(
1376 array('0' => 'One', '1' => 'Two', '3' => 'Three'),
1377 array(0 => 'One', 1 => 'Two', 2 => 'Three'),
1378 ),
1379 'returns correctly if just a single keys is not numeric' => array(
1380 array(0 => 'Zero', '1' => 'One', 'Two' => 'Two'),
1381 array(0 => 'Zero', '1' => 'One', 'Two' => 'Two'),
1382 ),
1383 'return self with nested numerically keyed array' => array(
1384 array(
1385 'One',
1386 'Two',
1387 'Three',
1388 array(
1389 'sub.One',
1390 'sub.Two',
1391 )
1392 ),
1393 array(
1394 'One',
1395 'Two',
1396 'Three',
1397 array(
1398 'sub.One',
1399 'sub.Two',
1400 )
1401 )
1402 ),
1403 'returns correctly with nested numerically keyed array with leaps' => array(
1404 array(
1405 'One',
1406 'Two',
1407 'Three',
1408 array(
1409 0 => 'sub.One',
1410 2 => 'sub.Two',
1411 )
1412 ),
1413 array(
1414 'One',
1415 'Two',
1416 'Three',
1417 array(
1418 'sub.One',
1419 'sub.Two',
1420 )
1421 )
1422 ),
1423 'returns correctly with nested string-keyed array' => array(
1424 array(
1425 'One',
1426 'Two',
1427 'Three',
1428 array(
1429 'one' => 'sub.One',
1430 'two' => 'sub.Two',
1431 )
1432 ),
1433 array(
1434 'One',
1435 'Two',
1436 'Three',
1437 array(
1438 'one' => 'sub.One',
1439 'two' => 'sub.Two',
1440 )
1441 )
1442 ),
1443 'returns correctly with deeply nested arrays' => array(
1444 array(
1445 'One',
1446 'Two',
1447 array(
1448 'one' => 1,
1449 'two' => 2,
1450 'three' => array(
1451 2 => 'SubSubOne',
1452 5 => 'SubSubTwo',
1453 9 => array(0,1,2),
1454 array()
1455 )
1456 )
1457 ),
1458 array(
1459 'One',
1460 'Two',
1461 array(
1462 'one' => 1,
1463 'two' => 2,
1464 'three' => array(
1465 'SubSubOne',
1466 'SubSubTwo',
1467 array(0,1,2),
1468 array()
1469 )
1470 )
1471 )
1472 )
1473 );
1474 }
1475
1476 /**
1477 * @test
1478 * @param array $inputArray
1479 * @param array $expected
1480 * @dataProvider renumberKeysToAvoidLeapsIfKeysAreAllNumericDataProvider
1481 */
1482 public function renumberKeysToAvoidLeapsIfKeysAreAllNumericReturnsExpectedOrder(array $inputArray, array $expected) {
1483 $this->assertEquals($expected, ArrayUtility::renumberKeysToAvoidLeapsIfKeysAreAllNumeric($inputArray));
1484 }
1485
1486 /**
1487 * @return array
1488 */
1489 public function mergeRecursiveWithOverruleCalculatesExpectedResultDataProvider() {
1490 return array(
1491 'Override array can reset string to array' => array(
1492 array(
1493 'first' => array(
1494 'second' => 'foo',
1495 ),
1496 ),
1497 array(
1498 'first' => array(
1499 'second' => array('third' => 'bar'),
1500 ),
1501 ),
1502 TRUE,
1503 TRUE,
1504 TRUE,
1505 array(
1506 'first' => array(
1507 'second' => array('third' => 'bar'),
1508 ),
1509 ),
1510 ),
1511 'Override array does not reset array to string (weird!)' => array(
1512 array(
1513 'first' => array(),
1514 ),
1515 array(
1516 'first' => 'foo',
1517 ),
1518 TRUE,
1519 TRUE,
1520 TRUE,
1521 array(
1522 'first' => array(), // This is rather unexpected, naive expectation: first => 'foo'
1523 ),
1524 ),
1525 'Override array does override string with null' => array(
1526 array(
1527 'first' => 'foo',
1528 ),
1529 array(
1530 'first' => NULL,
1531 ),
1532 TRUE,
1533 TRUE,
1534 TRUE,
1535 array(
1536 'first' => NULL,
1537 ),
1538 ),
1539 'Override array does override null with string' => array(
1540 array(
1541 'first' => NULL,
1542 ),
1543 array(
1544 'first' => 'foo',
1545 ),
1546 TRUE,
1547 TRUE,
1548 TRUE,
1549 array(
1550 'first' => 'foo',
1551 ),
1552 ),
1553 'Override array does override null with empty string' => array(
1554 array(
1555 'first' => NULL,
1556 ),
1557 array(
1558 'first' => '',
1559 ),
1560 TRUE,
1561 TRUE,
1562 TRUE,
1563 array(
1564 'first' => '',
1565 ),
1566 ),
1567 'Override array does not override string with NULL if requested' => array(
1568 array(
1569 'first' => 'foo',
1570 ),
1571 array(
1572 'first' => NULL,
1573 ),
1574 TRUE,
1575 FALSE, // no include empty values
1576 TRUE,
1577 array(
1578 'first' => 'foo',
1579 ),
1580 ),
1581 'Override array does override null with null' => array(
1582 array(
1583 'first' => NULL,
1584 ),
1585 array(
1586 'first' => NULL,
1587 ),
1588 TRUE,
1589 TRUE,
1590 TRUE,
1591 array(
1592 'first' => '',
1593 ),
1594 ),
1595 'Override array can __UNSET values' => array(
1596 array(
1597 'first' => array(
1598 'second' => 'second',
1599 'third' => 'third',
1600 ),
1601 'fifth' => array(),
1602 ),
1603 array(
1604 'first' => array(
1605 'second' => 'overrule',
1606 'third' => '__UNSET',
1607 'fourth' => 'overrile',
1608 ),
1609 'fifth' => '__UNSET',
1610 ),
1611 TRUE,
1612 TRUE,
1613 TRUE,
1614 array(
1615 'first' => array(
1616 'second' => 'overrule',
1617 'fourth' => 'overrile',
1618 ),
1619 ),
1620 ),
1621 'Override can add keys' => array(
1622 array(
1623 'first' => 'foo',
1624 ),
1625 array(
1626 'second' => 'bar',
1627 ),
1628 TRUE,
1629 TRUE,
1630 TRUE,
1631 array(
1632 'first' => 'foo',
1633 'second' => 'bar',
1634 ),
1635 ),
1636 'Override does not add key if __UNSET' => array(
1637 array(
1638 'first' => 'foo',
1639 ),
1640 array(
1641 'second' => '__UNSET',
1642 ),
1643 TRUE,
1644 TRUE,
1645 TRUE,
1646 array(
1647 'first' => 'foo',
1648 ),
1649 ),
1650 'Override does not add key if not requested' => array(
1651 array(
1652 'first' => 'foo',
1653 ),
1654 array(
1655 'second' => 'bar',
1656 ),
1657 FALSE, // no add keys
1658 TRUE,
1659 TRUE,
1660 array(
1661 'first' => 'foo',
1662 ),
1663 ),
1664 'Override does not add key if not requested with add include empty values' => array(
1665 array(
1666 'first' => 'foo',
1667 ),
1668 array(
1669 'second' => 'bar',
1670 ),
1671 FALSE, // no add keys
1672 FALSE, // no include empty values
1673 TRUE,
1674 array(
1675 'first' => 'foo',
1676 ),
1677 ),
1678 'Override does not override string with empty string if requested' => array(
1679 array(
1680 'first' => 'foo',
1681 ),
1682 array(
1683 'first' => '',
1684 ),
1685 TRUE,
1686 FALSE, // no include empty values
1687 TRUE,
1688 array(
1689 'first' => 'foo',
1690 ),
1691 ),
1692 'Override array does merge instead of __UNSET if requested (weird!)' => array(
1693 array(
1694 'first' => array(
1695 'second' => 'second',
1696 'third' => 'third',
1697 ),
1698 'fifth' => array(),
1699 ),
1700 array(
1701 'first' => array(
1702 'second' => 'overrule',
1703 'third' => '__UNSET',
1704 'fourth' => 'overrile',
1705 ),
1706 'fifth' => '__UNSET',
1707 ),
1708 TRUE,
1709 TRUE,
1710 FALSE,
1711 array(
1712 'first' => array(
1713 'second' => 'overrule',
1714 'third' => '__UNSET', // overruled
1715 'fourth' => 'overrile',
1716 ),
1717 'fifth' => array(), // not overruled with string here, naive expectation: 'fifth' => '__UNSET'
1718 ),
1719 ),
1720 );
1721 }
1722
1723 /**
1724 * @test
1725 * @dataProvider mergeRecursiveWithOverruleCalculatesExpectedResultDataProvider
1726 * @param array $input1 Input 1
1727 * @param array $input2 Input 2
1728 * @param bool $addKeys TRUE if should add keys, else FALSE
1729 * @param bool $includeEmptyValues TRUE if should include empty values, else FALSE
1730 * @param bool $enableUnsetFeature TRUE if should enable unset feature, else FALSE
1731 * @param array $expected expected array
1732 */
1733 public function mergeRecursiveWithOverruleCalculatesExpectedResult($input1, $input2, $addKeys, $includeEmptyValues, $enableUnsetFeature, $expected) {
1734 ArrayUtility::mergeRecursiveWithOverrule($input1, $input2, $addKeys, $includeEmptyValues, $enableUnsetFeature);
1735 $this->assertEquals($expected, $input1);
1736 }
1737
1738 //////////////////////////////////
1739 // Tests concerning inArray
1740 //////////////////////////////////
1741 /**
1742 * @test
1743 * @dataProvider inArrayDataProvider
1744 * @param array $array target array
1745 * @param string $item search string
1746 * @param bool $expected expected value
1747 */
1748 public function inArrayChecksStringExistenceWithinArray($array, $item, $expected) {
1749 $this->assertEquals($expected, ArrayUtility::inArray($array, $item));
1750 }
1751
1752 /**
1753 * Data provider for inArrayChecksStringExistenceWithinArray
1754 *
1755 * @return array
1756 */
1757 public function inArrayDataProvider() {
1758 return array(
1759 'Empty array' => array(array(), 'search', FALSE),
1760 'One item array no match' => array(array('one'), 'two', FALSE),
1761 'One item array match' => array(array('one'), 'one', TRUE),
1762 'Multiple items array no match' => array(array('one', 2, 'three', 4), 'four', FALSE),
1763 'Multiple items array match' => array(array('one', 2, 'three', 4), 'three', TRUE),
1764 'Integer search items can match string values' => array(array('0', '1', '2'), 1, TRUE),
1765 'Search item is not casted to integer for a match' => array(array(4), '4a', FALSE),
1766 'Empty item won\'t match - in contrast to the php-builtin ' => array(array(0, 1, 2), '', FALSE)
1767 );
1768 }
1769
1770 //////////////////////////////////
1771 // Tests concerning removeArrayEntryByValue
1772 //////////////////////////////////
1773 /**
1774 * @test
1775 */
1776 public function checkRemoveArrayEntryByValueRemovesEntriesFromOneDimensionalArray() {
1777 $inputArray = array(
1778 '0' => 'test1',
1779 '1' => 'test2',
1780 '2' => 'test3',
1781 '3' => 'test2'
1782 );
1783 $compareValue = 'test2';
1784 $expectedResult = array(
1785 '0' => 'test1',
1786 '2' => 'test3'
1787 );
1788 $actualResult = ArrayUtility::removeArrayEntryByValue($inputArray, $compareValue);
1789 $this->assertEquals($expectedResult, $actualResult);
1790 }
1791
1792 /**
1793 * @test
1794 */
1795 public function checkRemoveArrayEntryByValueRemovesEntriesFromMultiDimensionalArray() {
1796 $inputArray = array(
1797 '0' => 'foo',
1798 '1' => array(
1799 '10' => 'bar'
1800 ),
1801 '2' => 'bar'
1802 );
1803 $compareValue = 'bar';
1804 $expectedResult = array(
1805 '0' => 'foo',
1806 '1' => array()
1807 );
1808 $actualResult = ArrayUtility::removeArrayEntryByValue($inputArray, $compareValue);
1809 $this->assertEquals($expectedResult, $actualResult);
1810 }
1811
1812 /**
1813 * @test
1814 */
1815 public function checkRemoveArrayEntryByValueRemovesEntryWithEmptyString() {
1816 $inputArray = array(
1817 '0' => 'foo',
1818 '1' => '',
1819 '2' => 'bar'
1820 );
1821 $compareValue = '';
1822 $expectedResult = array(
1823 '0' => 'foo',
1824 '2' => 'bar'
1825 );
1826 $actualResult = ArrayUtility::removeArrayEntryByValue($inputArray, $compareValue);
1827 $this->assertEquals($expectedResult, $actualResult);
1828 }
1829
1830 //////////////////////////////////
1831 // Tests concerning keepItemsInArray
1832 //////////////////////////////////
1833 /**
1834 * @test
1835 * @dataProvider keepItemsInArrayWorksWithOneArgumentDataProvider
1836 * @param mixed $search The items which are allowed/kept in the array
1837 * @param array $array target array
1838 * @param array $expected expected array
1839 */
1840 public function keepItemsInArrayWorksWithOneArgument($search, $array, $expected) {
1841 $this->assertEquals($expected, ArrayUtility::keepItemsInArray($array, $search));
1842 }
1843
1844 /**
1845 * Data provider for keepItemsInArrayWorksWithOneArgument
1846 *
1847 * @return array
1848 */
1849 public function keepItemsInArrayWorksWithOneArgumentDataProvider() {
1850 $array = array(
1851 'one' => 'one',
1852 'two' => 'two',
1853 'three' => 'three'
1854 );
1855 return array(
1856 'Empty argument will match "all" elements' => array(NULL, $array, $array),
1857 'No match' => array('four', $array, array()),
1858 'One match' => array('two', $array, array('two' => 'two')),
1859 'Multiple matches' => array('two,one', $array, array('one' => 'one', 'two' => 'two')),
1860 'Argument can be an array' => array(array('three'), $array, array('three' => 'three'))
1861 );
1862 }
1863
1864 /**
1865 * Shows the exmaple from the doc comment where
1866 * a function is used to reduce the sub arrays to one item which
1867 * is then used for the matching.
1868 *
1869 * @test
1870 */
1871 public function keepItemsInArrayCanUseCallbackOnSearchArray() {
1872 $array = array(
1873 'aa' => array('first', 'second'),
1874 'bb' => array('third', 'fourth'),
1875 'cc' => array('fifth', 'sixth')
1876 );
1877 $expected = array('bb' => array('third', 'fourth'));
1878 $keepItems = 'third';
1879 $getValueFunc = create_function('$value', 'return $value[0];');
1880 $match = ArrayUtility::keepItemsInArray($array, $keepItems, $getValueFunc);
1881 $this->assertEquals($expected, $match);
1882 }
1883
1884 /**
1885 * Similar to keepItemsInArrayCanUseCallbackOnSearchArray(),
1886 * but uses a closure instead of create_function()
1887 *
1888 * @test
1889 */
1890 public function keepItemsInArrayCanUseClosure() {
1891 $array = array(
1892 'aa' => array('first', 'second'),
1893 'bb' => array('third', 'fourth'),
1894 'cc' => array('fifth', 'sixth')
1895 );
1896 $expected = array('bb' => array('third', 'fourth'));
1897 $keepItems = 'third';
1898 $match = ArrayUtility::keepItemsInArray(
1899 $array,
1900 $keepItems,
1901 function ($value) {
1902 return $value[0];
1903 }
1904 );
1905 $this->assertEquals($expected, $match);
1906 }
1907
1908 //////////////////////////////////
1909 // Tests concerning remapArrayKeys
1910 //////////////////////////////////
1911 /**
1912 * @test
1913 */
1914 public function remapArrayKeysExchangesKeysWithGivenMapping() {
1915 $array = array(
1916 'one' => 'one',
1917 'two' => 'two',
1918 'three' => 'three'
1919 );
1920 $keyMapping = array(
1921 'one' => '1',
1922 'two' => '2'
1923 );
1924 $expected = array(
1925 '1' => 'one',
1926 '2' => 'two',
1927 'three' => 'three'
1928 );
1929 ArrayUtility::remapArrayKeys($array, $keyMapping);
1930 $this->assertEquals($expected, $array);
1931 }
1932
1933 //////////////////////////////////////
1934 // Tests concerning arrayDiffAssocRecursive
1935 //////////////////////////////////////
1936 /**
1937 * @test
1938 */
1939 public function arrayDiffAssocRecursiveHandlesOneDimensionalArrays() {
1940 $array1 = array(
1941 'key1' => 'value1',
1942 'key2' => 'value2',
1943 'key3' => 'value3'
1944 );
1945 $array2 = array(
1946 'key1' => 'value1',
1947 'key3' => 'value3'
1948 );
1949 $expectedResult = array(
1950 'key2' => 'value2'
1951 );
1952 $actualResult = ArrayUtility::arrayDiffAssocRecursive($array1, $array2);
1953 $this->assertEquals($expectedResult, $actualResult);
1954 }
1955
1956 /**
1957 * @test
1958 */
1959 public function arrayDiffAssocRecursiveHandlesMultiDimensionalArrays() {
1960 $array1 = array(
1961 'key1' => 'value1',
1962 'key2' => array(
1963 'key21' => 'value21',
1964 'key22' => 'value22',
1965 'key23' => array(
1966 'key231' => 'value231',
1967 'key232' => 'value232'
1968 )
1969 )
1970 );
1971 $array2 = array(
1972 'key1' => 'value1',
1973 'key2' => array(
1974 'key21' => 'value21',
1975 'key23' => array(
1976 'key231' => 'value231'
1977 )
1978 )
1979 );
1980 $expectedResult = array(
1981 'key2' => array(
1982 'key22' => 'value22',
1983 'key23' => array(
1984 'key232' => 'value232'
1985 )
1986 )
1987 );
1988 $actualResult = ArrayUtility::arrayDiffAssocRecursive($array1, $array2);
1989 $this->assertEquals($expectedResult, $actualResult);
1990 }
1991
1992 /**
1993 * @test
1994 */
1995 public function arrayDiffAssocRecursiveHandlesMixedArrays() {
1996 $array1 = array(
1997 'key1' => array(
1998 'key11' => 'value11',
1999 'key12' => 'value12'
2000 ),
2001 'key2' => 'value2',
2002 'key3' => 'value3'
2003 );
2004 $array2 = array(
2005 'key1' => 'value1',
2006 'key2' => array(
2007 'key21' => 'value21'
2008 )
2009 );
2010 $expectedResult = array(
2011 'key3' => 'value3'
2012 );
2013 $actualResult = ArrayUtility::arrayDiffAssocRecursive($array1, $array2);
2014 $this->assertEquals($expectedResult, $actualResult);
2015 }
2016
2017 //////////////////////////////////////
2018 // Tests concerning naturalKeySortRecursive
2019 //////////////////////////////////////
2020
2021 /**
2022 * @test
2023 */
2024 public function naturalKeySortRecursiveSortsOneDimensionalArrayByNaturalOrder() {
2025 $testArray = array(
2026 'bb' => 'bb',
2027 'ab' => 'ab',
2028 '123' => '123',
2029 'aaa' => 'aaa',
2030 'abc' => 'abc',
2031 '23' => '23',
2032 'ba' => 'ba',
2033 'bad' => 'bad',
2034 '2' => '2',
2035 'zap' => 'zap',
2036 '210' => '210'
2037 );
2038 $expectedResult = array(
2039 '2',
2040 '23',
2041 '123',
2042 '210',
2043 'aaa',
2044 'ab',
2045 'abc',
2046 'ba',
2047 'bad',
2048 'bb',
2049 'zap'
2050 );
2051 ArrayUtility::naturalKeySortRecursive($testArray);
2052 $this->assertEquals($expectedResult, array_values($testArray));
2053 }
2054
2055 /**
2056 * @test
2057 */
2058 public function naturalKeySortRecursiveSortsMultiDimensionalArrayByNaturalOrder() {
2059 $testArray = array(
2060 '2' => '2',
2061 'bb' => 'bb',
2062 'ab' => 'ab',
2063 '23' => '23',
2064 'aaa' => array(
2065 'bb' => 'bb',
2066 'ab' => 'ab',
2067 '123' => '123',
2068 'aaa' => 'aaa',
2069 '2' => '2',
2070 'abc' => 'abc',
2071 'ba' => 'ba',
2072 '23' => '23',
2073 'bad' => array(
2074 'bb' => 'bb',
2075 'ab' => 'ab',
2076 '123' => '123',
2077 'aaa' => 'aaa',
2078 'abc' => 'abc',
2079 '23' => '23',
2080 'ba' => 'ba',
2081 'bad' => 'bad',
2082 '2' => '2',
2083 'zap' => 'zap',
2084 '210' => '210'
2085 ),
2086 '210' => '210',
2087 'zap' => 'zap'
2088 ),
2089 'abc' => 'abc',
2090 'ba' => 'ba',
2091 '210' => '210',
2092 'bad' => 'bad',
2093 '123' => '123',
2094 'zap' => 'zap'
2095 );
2096 $expectedResult = array(
2097 '2',
2098 '23',
2099 '123',
2100 '210',
2101 'aaa',
2102 'ab',
2103 'abc',
2104 'ba',
2105 'bad',
2106 'bb',
2107 'zap'
2108 );
2109 ArrayUtility::naturalKeySortRecursive($testArray);
2110 $this->assertEquals($expectedResult, array_values(array_keys($testArray['aaa']['bad'])));
2111 $this->assertEquals($expectedResult, array_values(array_keys($testArray['aaa'])));
2112 $this->assertEquals($expectedResult, array_values(array_keys($testArray)));
2113 }
2114 }