CollectionTest.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. <?php
  2. /**
  3. * Lithium: the most rad php framework
  4. *
  5. * @copyright Copyright 2013, Union of RAD (http://union-of-rad.org)
  6. * @license http://opensource.org/licenses/bsd-license.php The BSD License
  7. */
  8. namespace lithium\tests\cases\util;
  9. use stdClass;
  10. use lithium\data\Entity;
  11. use lithium\util\Collection;
  12. use lithium\tests\mocks\util\MockCollectionMarker;
  13. use lithium\tests\mocks\util\MockCollectionObject;
  14. use lithium\tests\mocks\util\MockCollectionStringCast;
  15. class CollectionTest extends \lithium\test\Unit {
  16. public function setUp() {
  17. Collection::formats('lithium\net\http\Media');
  18. }
  19. public function tearDown() {
  20. Collection::formats(false);
  21. }
  22. public function testArrayLike() {
  23. $collection = new Collection();
  24. $collection[] = 'foo';
  25. $this->assertEqual($collection[0], 'foo');
  26. $this->assertEqual(count($collection), 1);
  27. $collection = new Collection(array('data' => array('foo')));
  28. $this->assertEqual($collection[0], 'foo');
  29. $this->assertEqual(count($collection), 1);
  30. }
  31. public function testObjectMethodDispatch() {
  32. $collection = new Collection();
  33. for ($i = 0; $i < 10; $i++) {
  34. $collection[] = new MockCollectionMarker();
  35. }
  36. $result = $collection->mark();
  37. $this->assertEqual($result, array_fill(0, 10, true));
  38. $result = $collection->mapArray();
  39. $this->assertEqual($result, array_fill(0, 10, array('foo')));
  40. $result = $collection->invoke('mapArray', array(), array('merge' => true));
  41. $this->assertEqual($result, array_fill(0, 10, 'foo'));
  42. $collection = new Collection(array(
  43. 'data' => array_fill(0, 10, new MockCollectionObject())
  44. ));
  45. $result = $collection->testFoo();
  46. $this->assertEqual($result, array_fill(0, 10, 'testFoo'));
  47. $result = $collection->invoke('testFoo', array(), array('collect' => true));
  48. $this->assertTrue($result instanceof Collection);
  49. $this->assertEqual($result->to('array'), array_fill(0, 10, 'testFoo'));
  50. }
  51. public function testObjectCasting() {
  52. $collection = new Collection(array(
  53. 'data' => array_fill(0, 10, new MockCollectionObject())
  54. ));
  55. $result = $collection->to('array');
  56. $expected = array_fill(0, 10, array(1 => 2, 2 => 3));
  57. $this->assertEqual($expected, $result);
  58. $collection = new Collection(array(
  59. 'data' => array_fill(0, 10, new MockCollectionMarker())
  60. ));
  61. $result = $collection->to('array');
  62. $expected = array_fill(0, 10, array('marker' => false, 'data' => 'foo'));
  63. $this->assertEqual($expected, $result);
  64. $collection = new Collection(array(
  65. 'data' => array_fill(0, 10, new MockCollectionStringCast())
  66. ));
  67. $result = $collection->to('array');
  68. $expected = array_fill(0, 10, json_encode(array(1 => 2, 2 => 3)));
  69. $this->assertEqual($expected, $result);
  70. }
  71. /**
  72. * Tests that the `find()` method properly filters items out of the resulting collection.
  73. *
  74. * @return void
  75. */
  76. public function testCollectionFindFilter() {
  77. $collection = new Collection(array('data' => array_merge(
  78. array_fill(0, 10, 1),
  79. array_fill(0, 10, 2)
  80. )));
  81. $this->assertEqual(20, count($collection->to('array')));
  82. $filter = function($item) { return $item === 1; };
  83. $result = $collection->find($filter);
  84. $this->assertTrue($result instanceof Collection);
  85. $this->assertEqual(array_fill(0, 10, 1), $result->to('array'));
  86. $result = $collection->find($filter, array('collect' => false));
  87. $this->assertEqual(array_fill(0, 10, 1), $result);
  88. }
  89. /**
  90. * Tests that the `first()` method properly returns the first non-empty value.
  91. *
  92. * @return void
  93. */
  94. public function testCollectionFirstFilter() {
  95. $collection = new Collection(array('data' => array(0, 1, 2)));
  96. $result = $collection->first(function($value) { return $value; });
  97. $this->assertEqual(1, $result);
  98. $collection = new Collection(array('data' => array('Hello', '', 'Goodbye')));
  99. $result = $collection->first(function($value) { return $value; });
  100. $this->assertEqual('Hello', $result);
  101. $collection = new Collection(array('data' => array('', 'Hello', 'Goodbye')));
  102. $result = $collection->first(function($value) { return $value; });
  103. $this->assertEqual('Hello', $result);
  104. $collection = new Collection(array('data' => array('', 'Hello', 'Goodbye')));
  105. $result = $collection->first();
  106. $this->assertEqual('', $result);
  107. }
  108. /**
  109. * Tests that the `each()` filter applies the callback to each item in the current collection,
  110. * returning an instance of itself.
  111. *
  112. * @return void
  113. */
  114. public function testCollectionEachFilter() {
  115. $collection = new Collection(array('data' => array(1, 2, 3, 4, 5)));
  116. $filter = function($item) { return ++$item; };
  117. $result = $collection->each($filter);
  118. $this->assertIdentical($collection, $result);
  119. $this->assertEqual(array(2, 3, 4, 5, 6), $collection->to('array'));
  120. }
  121. public function testCollectionMapFilter() {
  122. $collection = new Collection(array('data' => array(1, 2, 3, 4, 5)));
  123. $filter = function($item) { return ++$item; };
  124. $result = $collection->map($filter);
  125. $this->assertNotEqual($collection, $result);
  126. $this->assertEqual(array(1, 2, 3, 4, 5), $collection->to('array'));
  127. $this->assertEqual(array(2, 3, 4, 5, 6), $result->to('array'));
  128. $result = $collection->map($filter, array('collect' => false));
  129. $this->assertEqual(array(2, 3, 4, 5, 6), $result);
  130. }
  131. public function testCollectionReduceFilter() {
  132. $collection = new Collection(array('data' => array(1, 2, 3)));
  133. $filter = function($memo, $item) { return $memo + $item; };
  134. $result = $collection->reduce($filter, 0);
  135. $this->assertEqual(6, $collection->reduce($filter, 0));
  136. $this->assertEqual(7, $collection->reduce($filter, 1));
  137. }
  138. /**
  139. * Tests the `ArrayAccess` interface implementation for manipulating values by direct offsets.
  140. *
  141. * @return void
  142. */
  143. public function testArrayAccessOffsetMethods() {
  144. $collection = new Collection(array('data' => array('foo', 'bar', 'baz' => 'dib')));
  145. $this->assertTrue($collection->offsetExists(0));
  146. $this->assertTrue($collection->offsetExists(1));
  147. $this->assertTrue($collection->offsetExists('0'));
  148. $this->assertTrue($collection->offsetExists('baz'));
  149. $this->assertFalse($collection->offsetExists('2'));
  150. $this->assertFalse($collection->offsetExists('bar'));
  151. $this->assertFalse($collection->offsetExists(2));
  152. $this->assertEqual('foo', $collection->offsetSet('bar', 'foo'));
  153. $this->assertTrue($collection->offsetExists('bar'));
  154. $this->assertNull($collection->offsetUnset('bar'));
  155. $this->assertFalse($collection->offsetExists('bar'));
  156. $data = array('Hello', 2, 3, null, 6, false, true, 0);
  157. $collection = new Collection(array('data' => $data));
  158. $cpt = 0;
  159. foreach ($collection as $i => $word) {
  160. $this->assertTrue(isset($collection[$cpt]));
  161. $cpt++;
  162. }
  163. $this->assertIdentical(8, $cpt);
  164. }
  165. /**
  166. * Tests the `ArrayAccess` interface implementation for traversing values.
  167. *
  168. * @return void
  169. */
  170. public function testArrayAccessTraversalMethods() {
  171. $collection = new Collection(array('data' => array('foo', 'bar', 'baz' => 'dib')));
  172. $this->assertEqual('foo', $collection->current());
  173. $this->assertEqual('bar', $collection->next());
  174. $this->assertEqual('foo', $collection->prev());
  175. $this->assertEqual('bar', $collection->next());
  176. $this->assertEqual('dib', $collection->next());
  177. $this->assertEqual('baz', $collection->key());
  178. $this->assertTrue($collection->valid());
  179. $this->assertFalse($collection->next());
  180. $this->assertFalse($collection->valid());
  181. $this->assertEqual('foo', $collection->rewind());
  182. $this->assertTrue($collection->valid());
  183. $this->assertEqual('dib', $collection->prev());
  184. $this->assertTrue($collection->valid());
  185. $this->assertEqual('bar', $collection->prev());
  186. $this->assertTrue($collection->valid());
  187. $this->assertEqual('dib', $collection->end());
  188. $this->assertTrue($collection->valid());
  189. $collection = new Collection(array('data' => array(0, 1, 2, 3, 4)));
  190. $this->assertIdentical(0, $collection->first());
  191. $this->assertIdentical(0, $collection->rewind());
  192. $this->assertIdentical(1, $collection->next());
  193. $this->assertIdentical(2, $collection->next());
  194. $this->assertIdentical(3, $collection->next());
  195. $this->assertIdentical(2, $collection->prev());
  196. $this->assertIdentical(2, $collection->current());
  197. $this->assertIdentical(3, $collection->next());
  198. $this->assertIdentical(4, $collection->next());
  199. $this->assertIdentical(3, $collection->prev());
  200. $this->assertIdentical(4, $collection->next());
  201. $this->assertTrue($collection->valid());
  202. $this->assertFalse($collection->next());
  203. $this->assertFalse($collection->valid());
  204. $this->assertFalse($collection->current());
  205. $this->assertIdentical(4, $collection->prev());
  206. $this->assertTrue($collection->valid());
  207. }
  208. /**
  209. * Tests objects and scalar values being appended to the collection.
  210. *
  211. * @return void
  212. */
  213. public function testValueAppend() {
  214. $collection = new Collection();
  215. $this->assertFalse($collection->valid());
  216. $this->assertEqual(0, count($collection));
  217. $collection->append(1);
  218. $this->assertEqual(1, count($collection));
  219. $collection->append(new stdClass());
  220. $this->assertEqual(2, count($collection));
  221. $this->assertEqual(1, $collection->current());
  222. $this->assertEqual(new stdClass(), $collection->next());
  223. }
  224. /**
  225. * Tests getting the index of the internal array.
  226. *
  227. * @return void
  228. */
  229. public function testInternalKeys() {
  230. $collection = new Collection(array('data' => array('foo', 'bar', 'baz' => 'dib')));
  231. $this->assertEqual(array(0, 1, 'baz'), $collection->keys());
  232. }
  233. /**
  234. * Tests that various types of handlers can be registered with `Collection::formats()`, and
  235. * that collection instances are converted correctly.
  236. *
  237. * @return void
  238. */
  239. public function testCollectionFormatConversion() {
  240. Collection::formats('lithium\net\http\Media');
  241. $data = array('hello', 'goodbye', 'foo' => array('bar', 'baz' => 'dib'));
  242. $collection = new Collection(compact('data'));
  243. $expected = json_encode($data);
  244. $result = $collection->to('json');
  245. $this->assertEqual($expected, $result);
  246. $this->assertNull($collection->to('badness'));
  247. Collection::formats(false);
  248. $this->assertNull($collection->to('json'));
  249. Collection::formats('json', function($collection, $options) {
  250. return json_encode($collection->to('array'));
  251. });
  252. $result = $collection->to('json');
  253. $this->assertEqual($expected, $result);
  254. $result = $collection->to(function($collection) {
  255. $value = array_map(
  256. function($i) { return is_array($i) ? join(',', $i) : $i; }, $collection->to('array')
  257. );
  258. return join(',', $value);
  259. });
  260. $expected = 'hello,goodbye,bar,dib';
  261. $this->assertEqual($expected, $result);
  262. }
  263. public function testCollectionHandlers() {
  264. $obj = new stdClass();
  265. $obj->a = "b";
  266. $handlers = array('stdClass' => function($v) { return (array) $v; });
  267. $data = array('test' => new Collection(array('data' => compact('obj')))) + compact('obj');
  268. $collection = new Collection(compact('data'));
  269. $expected = array(
  270. 'test' => array('obj' => array('a' => 'b')),
  271. 'obj' => array('a' => 'b')
  272. );
  273. $this->assertIdentical($expected, $collection->to('array', compact('handlers')));
  274. $handlers = array('stdClass' => function($v) { return $v; });
  275. $expected = array('test' => compact('obj')) + compact('obj');
  276. $this->assertIdentical($expected, $collection->to('array', compact('handlers')));
  277. }
  278. /**
  279. * Tests that the Collection::sort method works appropriately.
  280. */
  281. public function testCollectionSort() {
  282. $collection = new Collection(array('data' => array(5,3,4,1,2)));
  283. $collection->sort();
  284. $expected = array(1,2,3,4,5);
  285. $this->assertEqual($expected, $collection->to('array'));
  286. $collection = new Collection(array('data' => array('alan', 'dave', 'betsy', 'carl')));
  287. $expected = array('alan','betsy','carl','dave');
  288. $this->assertEqual($expected, $collection->sort()->to('array'));
  289. $collection = new Collection(array('data' => array('Alan', 'Dave', 'betsy', 'carl')));
  290. $expected = array('Alan', 'betsy', 'carl', 'Dave');
  291. $this->assertEqual($expected, $collection->sort('strcasecmp')->to('array'));
  292. $collection = new Collection(array('data' => array(5,3,4,1,2)));
  293. $collection->sort(function ($a,$b) {
  294. if ($a === $b) {
  295. return 0;
  296. }
  297. return ($b > $a ? 1 : -1);
  298. });
  299. $expected = array(5,4,3,2,1);
  300. $this->assertEqual($expected, $collection->to('array'));
  301. $collection = new Collection(array('data' => array(5,3,4,1,2)));
  302. $result = $collection->sort('blahgah');
  303. $this->assertEqual($collection->to('array'), $result->to('array'));
  304. }
  305. public function testUnsetInForeach() {
  306. $data = array('Delete me');
  307. $collection = new Collection(array('data' => $data));
  308. $this->assertIdentical($data, $collection->to('array'));
  309. $cpt = 0;
  310. foreach ($collection as $i => $word) {
  311. if ($word === 'Delete me') {
  312. unset($collection[$i]);
  313. }
  314. $cpt++;
  315. }
  316. $this->assertEqual(1, $cpt);
  317. $this->assertIdentical(array(), $collection->to('array'));
  318. $data = array(
  319. 'Hello',
  320. 'Delete me',
  321. 'Delete me',
  322. 'Delete me',
  323. 'Delete me',
  324. 'Delete me',
  325. 'Hello again!',
  326. 'Delete me'
  327. );
  328. $collection = new Collection(array('data' => $data));
  329. $this->assertIdentical($data, $collection->to('array'));
  330. foreach ($collection as $i => $word) {
  331. if ($word === 'Delete me') {
  332. unset($collection[$i]);
  333. }
  334. }
  335. $expected = array(0 => 'Hello', 6 => 'Hello again!');
  336. $results = $collection->to('array');
  337. $this->assertIdentical($expected, $results);
  338. $data = array(
  339. 'Delete me',
  340. 'Hello',
  341. 'Delete me',
  342. 'Delete me',
  343. 'Delete me',
  344. 'Delete me',
  345. 'Hello again!',
  346. 'Delete me'
  347. );
  348. $collection = new Collection(array('data' => $data));
  349. $this->assertIdentical($data, $collection->to('array'));
  350. foreach ($collection as $i => $word) {
  351. if ($word === 'Delete me') {
  352. unset($collection[$i]);
  353. }
  354. }
  355. $expected = array(1 => 'Hello', 6 => 'Hello again!');
  356. $results = $collection->to('array');
  357. $this->assertIdentical($expected, $results);
  358. }
  359. public function testCount() {
  360. $collection = new Collection(array('data' => array(5, 3, 4, 1, 2)));
  361. $this->assertIdentical(5, count($collection));
  362. $collection = new Collection(array('data' => array()));
  363. $this->assertIdentical(0, count($collection));
  364. $collection = new Collection(array('data' => array(5 ,null, 4, true, false, 'bob')));
  365. $this->assertIdentical(6, count($collection));
  366. unset($collection[1]);
  367. unset($collection[2]);
  368. $this->assertIdentical(4, count($collection));
  369. $first = (object) array('name' => 'First');
  370. $second = (object) array('name' => 'Second');
  371. $third = (object) array('name' => 'Third');
  372. $doc = new Collection(array(
  373. 'data' => array($first, $second, $third)
  374. ));
  375. $this->assertTrue(is_object($doc[0]));
  376. $this->assertTrue(is_object($doc[1]));
  377. $this->assertTrue(is_object($doc[2]));
  378. $this->assertEqual(3, count($doc));
  379. }
  380. public function testValid() {
  381. $collection = new Collection();
  382. $this->assertFalse($collection->valid());
  383. $collection = new Collection(array('data' => array(1, 5)));
  384. $this->assertTrue($collection->valid());
  385. }
  386. public function testRespondsToParent() {
  387. $collection = new Collection();
  388. $this->assertTrue($collection->respondsTo('applyFilter'));
  389. $this->assertFalse($collection->respondsTo('fooBarBaz'));
  390. }
  391. public function testRespondsToMagic() {
  392. $collection = new Collection(array(
  393. 'data' => array(
  394. new Entity(array(
  395. 'model' => 'lithium\tests\mocks\data\MockPost',
  396. 'data' => array('stats' => array('foo' => 'bar')),
  397. ))
  398. )
  399. ));
  400. $this->assertTrue($collection->respondsTo('instances'));
  401. $this->assertTrue($collection->respondsTo('foobar'));
  402. $this->assertFalse($collection->respondsTo('foobarbaz'));
  403. }
  404. }
  405. ?>