ArrTest.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. <?php defined('SYSPATH') OR die('Kohana bootstrap needs to be included before tests run');
  2. /**
  3. * Tests the Arr lib that's shipped with kohana
  4. *
  5. * @group kohana
  6. * @group kohana.core
  7. * @group kohana.core.arr
  8. *
  9. * @package Kohana
  10. * @category Tests
  11. * @author Kohana Team
  12. * @author BRMatt <[email protected]>
  13. * @copyright (c) 2008-2012 Kohana Team
  14. * @license http://kohanaframework.org/license
  15. */
  16. class Kohana_ArrTest extends Unittest_TestCase
  17. {
  18. /**
  19. * Provides test data for test_callback()
  20. *
  21. * @return array
  22. */
  23. public function provider_callback()
  24. {
  25. return array(
  26. // Tests....
  27. // That no parameters returns null
  28. array('function', array('function', NULL)),
  29. // That we can get an array of parameters values
  30. array('function(1,2,3)', array('function', array('1', '2', '3'))),
  31. // That it's not just using the callback "function"
  32. array('different_name(harry,jerry)', array('different_name', array('harry', 'jerry'))),
  33. // That static callbacks are parsed into arrays
  34. array('kohana::appify(this)', array(array('kohana', 'appify'), array('this'))),
  35. // Spaces are preserved in parameters
  36. array('deal::make(me, my mate )', array(array('deal', 'make'), array('me', ' my mate ')))
  37. // TODO: add more cases
  38. );
  39. }
  40. /**
  41. * Tests Arr::callback()
  42. *
  43. * @test
  44. * @dataProvider provider_callback
  45. * @param string $str String to parse
  46. * @param array $expected Callback and its parameters
  47. */
  48. public function test_callback($str, $expected)
  49. {
  50. $result = Arr::callback($str);
  51. $this->assertSame(2, count($result));
  52. $this->assertSame($expected, $result);
  53. }
  54. /**
  55. * Provides test data for test_extract
  56. *
  57. * @return array
  58. */
  59. public function provider_extract()
  60. {
  61. return array(
  62. array(
  63. array('kohana' => 'awesome', 'blueflame' => 'was'),
  64. array('kohana', 'cakephp', 'symfony'),
  65. NULL,
  66. array('kohana' => 'awesome', 'cakephp' => NULL, 'symfony' => NULL)
  67. ),
  68. // I realise noone should EVER code like this in real life,
  69. // but unit testing is very very very very boring
  70. array(
  71. array('chocolate cake' => 'in stock', 'carrot cake' => 'in stock'),
  72. array('carrot cake', 'humble pie'),
  73. 'not in stock',
  74. array('carrot cake' => 'in stock', 'humble pie' => 'not in stock'),
  75. ),
  76. array(
  77. // Source Array
  78. array('level1' => array('level2a' => 'value 1', 'level2b' => 'value 2')),
  79. // Paths to extract
  80. array('level1.level2a', 'level1.level2b'),
  81. // Default
  82. NULL,
  83. // Expected Result
  84. array('level1' => array('level2a' => 'value 1', 'level2b' => 'value 2')),
  85. ),
  86. array(
  87. // Source Array
  88. array('level1a' => array('level2a' => 'value 1'), 'level1b' => array('level2b' => 'value 2')),
  89. // Paths to extract
  90. array('level1a', 'level1b.level2b'),
  91. // Default
  92. NULL,
  93. // Expected Result
  94. array('level1a' => array('level2a' => 'value 1'), 'level1b' => array('level2b' => 'value 2')),
  95. ),
  96. array(
  97. // Source Array
  98. array('level1a' => array('level2a' => 'value 1'), 'level1b' => array('level2b' => 'value 2')),
  99. // Paths to extract
  100. array('level1a', 'level1b.level2b', 'level1c', 'level1d.notfound'),
  101. // Default
  102. 'default',
  103. // Expected Result
  104. array('level1a' => array('level2a' => 'value 1'), 'level1b' => array('level2b' => 'value 2'), 'level1c' => 'default', 'level1d' => array('notfound' => 'default')),
  105. ),
  106. );
  107. }
  108. /**
  109. * Tests Arr::extract()
  110. *
  111. * @test
  112. * @dataProvider provider_extract
  113. * @param array $array
  114. * @param array $paths
  115. * @param mixed $default
  116. * @param array $expected
  117. */
  118. public function test_extract(array $array, array $paths, $default, $expected)
  119. {
  120. $array = Arr::extract($array, $paths, $default);
  121. $this->assertSame(count($expected), count($array));
  122. $this->assertSame($expected, $array);
  123. }
  124. /**
  125. * Provides test data for test_pluck
  126. *
  127. * @return array
  128. */
  129. public function provider_pluck()
  130. {
  131. return array(
  132. array(
  133. array(
  134. array('id' => 20, 'name' => 'John Smith'),
  135. array('name' => 'Linda'),
  136. array('id' => 25, 'name' => 'Fred'),
  137. ),
  138. 'id',
  139. array(20, 25)
  140. ),
  141. );
  142. }
  143. /**
  144. * Tests Arr::pluck()
  145. *
  146. * @test
  147. * @dataProvider provider_pluck
  148. * @param array $array
  149. * @param string $key
  150. * @param array $expected
  151. */
  152. public function test_pluck(array $array, $key, $expected)
  153. {
  154. $array = Arr::pluck($array, $key);
  155. $this->assertSame(count($expected), count($array));
  156. $this->assertSame($expected, $array);
  157. }
  158. /**
  159. * Provides test data for test_get()
  160. *
  161. * @return array
  162. */
  163. public function provider_get()
  164. {
  165. return array(
  166. array(array('uno', 'dos', 'tress'), 1, NULL, 'dos'),
  167. array(array('we' => 'can', 'make' => 'change'), 'we', NULL, 'can'),
  168. array(array('uno', 'dos', 'tress'), 10, NULL, NULL),
  169. array(array('we' => 'can', 'make' => 'change'), 'he', NULL, NULL),
  170. array(array('we' => 'can', 'make' => 'change'), 'he', 'who', 'who'),
  171. array(array('we' => 'can', 'make' => 'change'), 'he', array('arrays'), array('arrays')),
  172. );
  173. }
  174. /**
  175. * Tests Arr::get()
  176. *
  177. * @test
  178. * @dataProvider provider_get()
  179. * @param array $array Array to look in
  180. * @param string|integer $key Key to look for
  181. * @param mixed $default What to return if $key isn't set
  182. * @param mixed $expected The expected value returned
  183. */
  184. public function test_get(array $array, $key, $default, $expected)
  185. {
  186. $this->assertSame(
  187. $expected,
  188. Arr::get($array, $key, $default)
  189. );
  190. }
  191. /**
  192. * Provides test data for test_is_assoc()
  193. *
  194. * @return array
  195. */
  196. public function provider_is_assoc()
  197. {
  198. return array(
  199. array(array('one', 'two', 'three'), FALSE),
  200. array(array('one' => 'o clock', 'two' => 'o clock', 'three' => 'o clock'), TRUE),
  201. );
  202. }
  203. /**
  204. * Tests Arr::is_assoc()
  205. *
  206. * @test
  207. * @dataProvider provider_is_assoc
  208. * @param array $array Array to check
  209. * @param boolean $expected Is $array assoc
  210. */
  211. public function test_is_assoc(array $array, $expected)
  212. {
  213. $this->assertSame(
  214. $expected,
  215. Arr::is_assoc($array)
  216. );
  217. }
  218. /**
  219. * Provides test data for test_is_array()
  220. *
  221. * @return array
  222. */
  223. public function provider_is_array()
  224. {
  225. return array(
  226. array($a = array('one', 'two', 'three'), TRUE),
  227. array(new ArrayObject($a), TRUE),
  228. array(new ArrayIterator($a), TRUE),
  229. array('not an array', FALSE),
  230. array(new stdClass, FALSE),
  231. );
  232. }
  233. /**
  234. * Tests Arr::is_array()
  235. *
  236. * @test
  237. * @dataProvider provider_is_array
  238. * @param mixed $value Value to check
  239. * @param boolean $expected Is $value an array?
  240. */
  241. public function test_is_array($array, $expected)
  242. {
  243. $this->assertSame(
  244. $expected,
  245. Arr::is_array($array)
  246. );
  247. }
  248. public function provider_merge()
  249. {
  250. return array(
  251. // Test how it merges arrays and sub arrays with assoc keys
  252. array(
  253. array('name' => 'mary', 'children' => array('fred', 'paul', 'sally', 'jane')),
  254. array('name' => 'john', 'children' => array('fred', 'paul', 'sally', 'jane')),
  255. array('name' => 'mary', 'children' => array('jane')),
  256. ),
  257. // See how it merges sub-arrays with numerical indexes
  258. array(
  259. array(array('test1'), array('test2'), array('test3')),
  260. array(array('test1'), array('test2')),
  261. array(array('test2'), array('test3')),
  262. ),
  263. array(
  264. array(array(array('test1')), array(array('test2')), array(array('test3'))),
  265. array(array(array('test1')), array(array('test2'))),
  266. array(array(array('test2')), array(array('test3'))),
  267. ),
  268. array(
  269. array('a' => array('test1','test2'), 'b' => array('test2','test3')),
  270. array('a' => array('test1'), 'b' => array('test2')),
  271. array('a' => array('test2'), 'b' => array('test3')),
  272. ),
  273. array(
  274. array('digits' => array(0, 1, 2, 3)),
  275. array('digits' => array(0, 1)),
  276. array('digits' => array(2, 3)),
  277. ),
  278. // See how it manages merging items with numerical indexes
  279. array(
  280. array(0, 1, 2, 3),
  281. array(0, 1),
  282. array(2, 3),
  283. ),
  284. // Try and get it to merge assoc. arrays recursively
  285. array(
  286. array('foo' => 'bar', array('temp' => 'life')),
  287. array('foo' => 'bin', array('temp' => 'name')),
  288. array('foo' => 'bar', array('temp' => 'life')),
  289. ),
  290. // Bug #3139
  291. array(
  292. array('foo' => array('bar')),
  293. array('foo' => 'bar'),
  294. array('foo' => array('bar')),
  295. ),
  296. array(
  297. array('foo' => 'bar'),
  298. array('foo' => array('bar')),
  299. array('foo' => 'bar'),
  300. ),
  301. // data set #9
  302. // Associative, Associative
  303. array(
  304. array('a' => 'K', 'b' => 'K', 'c' => 'L'),
  305. array('a' => 'J', 'b' => 'K'),
  306. array('a' => 'K', 'c' => 'L'),
  307. ),
  308. // Associative, Indexed
  309. array(
  310. array('a' => 'J', 'b' => 'K', 'L'),
  311. array('a' => 'J', 'b' => 'K'),
  312. array('K', 'L'),
  313. ),
  314. // Associative, Mixed
  315. array(
  316. array('a' => 'J', 'b' => 'K', 'K', 'c' => 'L'),
  317. array('a' => 'J', 'b' => 'K'),
  318. array('K', 'c' => 'L'),
  319. ),
  320. // data set #12
  321. // Indexed, Associative
  322. array(
  323. array('J', 'K', 'a' => 'K', 'c' => 'L'),
  324. array('J', 'K'),
  325. array('a' => 'K', 'c' => 'L'),
  326. ),
  327. // Indexed, Indexed
  328. array(
  329. array('J', 'K', 'L'),
  330. array('J', 'K'),
  331. array('K', 'L'),
  332. ),
  333. // Indexed, Mixed
  334. array(
  335. array('K', 'K', 'c' => 'L'),
  336. array('J', 'K'),
  337. array('K', 'c' => 'L'),
  338. ),
  339. // data set #15
  340. // Mixed, Associative
  341. array(
  342. array('a' => 'K', 'K', 'c' => 'L'),
  343. array('a' => 'J', 'K'),
  344. array('a' => 'K', 'c' => 'L'),
  345. ),
  346. // Mixed, Indexed
  347. array(
  348. array('a' => 'J', 'K', 'L'),
  349. array('a' => 'J', 'K'),
  350. array('J', 'L'),
  351. ),
  352. // Mixed, Mixed
  353. array(
  354. array('a' => 'K', 'L'),
  355. array('a' => 'J', 'K'),
  356. array('a' => 'K', 'L'),
  357. ),
  358. // Bug #3141
  359. array(
  360. array('servers' => array(array('1.1.1.1', 4730), array('2.2.2.2', 4730))),
  361. array('servers' => array(array('1.1.1.1', 4730))),
  362. array('servers' => array(array('2.2.2.2', 4730))),
  363. ),
  364. );
  365. }
  366. /**
  367. *
  368. * @test
  369. * @dataProvider provider_merge
  370. */
  371. public function test_merge($expected, $array1, $array2)
  372. {
  373. $this->assertSame(
  374. $expected,
  375. Arr::merge($array1,$array2)
  376. );
  377. }
  378. /**
  379. * Provides test data for test_path()
  380. *
  381. * @return array
  382. */
  383. public function provider_path()
  384. {
  385. $array = array(
  386. 'foobar' => array('definition' => 'lost'),
  387. 'kohana' => 'awesome',
  388. 'users' => array(
  389. 1 => array('name' => 'matt'),
  390. 2 => array('name' => 'john', 'interests' => array('hocky' => array('length' => 2), 'football' => array())),
  391. 3 => 'frank', // Issue #3194
  392. ),
  393. 'object' => new ArrayObject(array('iterator' => TRUE)), // Iterable object should work exactly the same
  394. );
  395. return array(
  396. // Tests returns normal values
  397. array($array['foobar'], $array, 'foobar'),
  398. array($array['kohana'], $array, 'kohana'),
  399. array($array['foobar']['definition'], $array, 'foobar.definition'),
  400. // Custom delimiters
  401. array($array['foobar']['definition'], $array, 'foobar/definition', NULL, '/'),
  402. // We should be able to use NULL as a default, returned if the key DNX
  403. array(NULL, $array, 'foobar.alternatives', NULL),
  404. array(NULL, $array, 'kohana.alternatives', NULL),
  405. // Try using a string as a default
  406. array('nothing', $array, 'kohana.alternatives', 'nothing'),
  407. // Make sure you can use arrays as defaults
  408. array(array('far', 'wide'), $array, 'cheese.origins', array('far', 'wide')),
  409. // Ensures path() casts ints to actual integers for keys
  410. array($array['users'][1]['name'], $array, 'users.1.name'),
  411. // Test that a wildcard returns the entire array at that "level"
  412. array($array['users'], $array, 'users.*'),
  413. // Now we check that keys after a wilcard will be processed
  414. array(array(0 => array(0 => 2)), $array, 'users.*.interests.*.length'),
  415. // See what happens when it can't dig any deeper from a wildcard
  416. array(NULL, $array, 'users.*.fans'),
  417. // Starting wildcards, issue #3269
  418. array(array('matt', 'john'), $array['users'], '*.name'),
  419. // Path as array, issue #3260
  420. array($array['users'][2]['name'], $array, array('users', 2, 'name')),
  421. array($array['object']['iterator'], $array, 'object.iterator'),
  422. );
  423. }
  424. /**
  425. * Tests Arr::path()
  426. *
  427. * @test
  428. * @dataProvider provider_path
  429. * @param string $path The path to follow
  430. * @param mixed $default The value to return if dnx
  431. * @param boolean $expected The expected value
  432. * @param string $delimiter The path delimiter
  433. */
  434. public function test_path($expected, $array, $path, $default = NULL, $delimiter = NULL)
  435. {
  436. $this->assertSame(
  437. $expected,
  438. Arr::path($array, $path, $default, $delimiter)
  439. );
  440. }
  441. /**
  442. * Provides test data for test_path()
  443. *
  444. * @return array
  445. */
  446. public function provider_set_path()
  447. {
  448. return array(
  449. // Tests returns normal values
  450. array(array('foo' => 'bar'), array(), 'foo', 'bar'),
  451. array(array('kohana' => array('is' => 'awesome')), array(), 'kohana.is', 'awesome'),
  452. array(array('kohana' => array('is' => 'cool', 'and' => 'slow')),
  453. array('kohana' => array('is' => 'cool')), 'kohana.and', 'slow'),
  454. // Custom delimiters
  455. array(array('kohana' => array('is' => 'awesome')), array(), 'kohana/is', 'awesome', '/'),
  456. // Ensures set_path() casts ints to actual integers for keys
  457. array(array('foo' => array('bar')), array('foo' => array('test')), 'foo.0', 'bar'),
  458. );
  459. }
  460. /**
  461. * Tests Arr::path()
  462. *
  463. * @test
  464. * @dataProvider provider_set_path
  465. * @param string $path The path to follow
  466. * @param boolean $expected The expected value
  467. * @param string $delimiter The path delimiter
  468. */
  469. public function test_set_path($expected, $array, $path, $value, $delimiter = NULL)
  470. {
  471. Arr::set_path($array, $path, $value, $delimiter);
  472. $this->assertSame($expected, $array);
  473. }
  474. /**
  475. * Provides test data for test_range()
  476. *
  477. * @return array
  478. */
  479. public function provider_range()
  480. {
  481. return array(
  482. array(1, 2),
  483. array(1, 100),
  484. array(25, 10),
  485. );
  486. }
  487. /**
  488. * Tests Arr::range()
  489. *
  490. * @dataProvider provider_range
  491. * @param integer $step The step between each value in the array
  492. * @param integer $max The max value of the range (inclusive)
  493. */
  494. public function test_range($step, $max)
  495. {
  496. $range = Arr::range($step, $max);
  497. $this->assertSame( (int) floor($max / $step), count($range));
  498. $current = $step;
  499. foreach ($range as $key => $value)
  500. {
  501. $this->assertSame($key, $value);
  502. $this->assertSame($current, $key);
  503. $this->assertLessThanOrEqual($max, $key);
  504. $current += $step;
  505. }
  506. }
  507. /**
  508. * Provides test data for test_unshift()
  509. *
  510. * @return array
  511. */
  512. public function provider_unshift()
  513. {
  514. return array(
  515. array(array('one' => '1', 'two' => '2',), 'zero', '0'),
  516. array(array('step 1', 'step 2', 'step 3'), 'step 0', 'wow')
  517. );
  518. }
  519. /**
  520. * Tests Arr::unshift()
  521. *
  522. * @test
  523. * @dataProvider provider_unshift
  524. * @param array $array
  525. * @param string $key
  526. * @param mixed $value
  527. */
  528. public function test_unshift(array $array, $key, $value)
  529. {
  530. $original = $array;
  531. Arr::unshift($array, $key, $value);
  532. $this->assertNotSame($original, $array);
  533. $this->assertSame(count($original) + 1, count($array));
  534. $this->assertArrayHasKey($key, $array);
  535. $this->assertSame($value, reset($array));
  536. $this->assertSame(key($array), $key);
  537. }
  538. /**
  539. * Provies test data for test_overwrite
  540. *
  541. * @return array Test Data
  542. */
  543. public function provider_overwrite()
  544. {
  545. return array(
  546. array(
  547. array('name' => 'Henry', 'mood' => 'tired', 'food' => 'waffles', 'sport' => 'checkers'),
  548. array('name' => 'John', 'mood' => 'bored', 'food' => 'bacon', 'sport' => 'checkers'),
  549. array('name' => 'Matt', 'mood' => 'tired', 'food' => 'waffles'),
  550. array('name' => 'Henry', 'age' => 18,),
  551. ),
  552. );
  553. }
  554. /**
  555. *
  556. * @test
  557. * @dataProvider provider_overwrite
  558. */
  559. public function test_overwrite($expected, $arr1, $arr2, $arr3 = array(), $arr4 = array())
  560. {
  561. $this->assertSame(
  562. $expected,
  563. Arr::overwrite($arr1, $arr2, $arr3, $arr4)
  564. );
  565. }
  566. /**
  567. * Provides test data for test_map
  568. *
  569. * @return array Test Data
  570. */
  571. public function provider_map()
  572. {
  573. return array(
  574. array('strip_tags', array('<p>foobar</p>'), NULL, array('foobar')),
  575. array('strip_tags', array(array('<p>foobar</p>'), array('<p>foobar</p>')), NULL, array(array('foobar'), array('foobar'))),
  576. array(
  577. 'strip_tags',
  578. array(
  579. 'foo' => '<p>foobar</p>',
  580. 'bar' => '<p>foobar</p>',
  581. ),
  582. NULL,
  583. array(
  584. 'foo' => 'foobar',
  585. 'bar' => 'foobar',
  586. ),
  587. ),
  588. array(
  589. 'strip_tags',
  590. array(
  591. 'foo' => '<p>foobar</p>',
  592. 'bar' => '<p>foobar</p>',
  593. ),
  594. array('foo'),
  595. array(
  596. 'foo' => 'foobar',
  597. 'bar' => '<p>foobar</p>',
  598. ),
  599. ),
  600. array(
  601. array(
  602. 'strip_tags',
  603. 'trim',
  604. ),
  605. array(
  606. 'foo' => '<p>foobar </p>',
  607. 'bar' => '<p>foobar</p>',
  608. ),
  609. NULL,
  610. array(
  611. 'foo' => 'foobar',
  612. 'bar' => 'foobar',
  613. ),
  614. ),
  615. );
  616. }
  617. /**
  618. *
  619. * @test
  620. * @dataProvider provider_map
  621. */
  622. public function test_map($method, $source, $keys, $expected)
  623. {
  624. $this->assertSame(
  625. $expected,
  626. Arr::map($method, $source, $keys)
  627. );
  628. }
  629. /**
  630. * Provides test data for test_flatten
  631. *
  632. * @return array Test Data
  633. */
  634. public function provider_flatten()
  635. {
  636. return array(
  637. array(array('set' => array('one' => 'something'), 'two' => 'other'), array('one' => 'something', 'two' => 'other')),
  638. );
  639. }
  640. /**
  641. *
  642. * @test
  643. * @dataProvider provider_flatten
  644. */
  645. public function test_flatten($source, $expected)
  646. {
  647. $this->assertSame(
  648. $expected,
  649. Arr::flatten($source)
  650. );
  651. }
  652. }