SortIteratorTest.php 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * For full copyright and license information, please see the LICENSE.txt
  9. * Redistributions of files must retain the above copyright notice.
  10. *
  11. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  12. * @link https://cakephp.org CakePHP(tm) Project
  13. * @since 3.0.0
  14. * @license https://opensource.org/licenses/mit-license.php MIT License
  15. */
  16. namespace Cake\Test\TestCase\Collection\Iterator;
  17. use ArrayObject;
  18. use Cake\Chronos\Chronos;
  19. use Cake\Chronos\ChronosDate;
  20. use Cake\Chronos\ChronosTime;
  21. use Cake\Collection\Iterator\SortIterator;
  22. use Cake\TestSuite\TestCase;
  23. use DateInterval;
  24. use DateTime;
  25. use DateTimeImmutable;
  26. use const SORT_ASC;
  27. use const SORT_DESC;
  28. use const SORT_NUMERIC;
  29. /**
  30. * SortIterator Test
  31. */
  32. class SortIteratorTest extends TestCase
  33. {
  34. /**
  35. * Tests sorting numbers with an identity callbacks
  36. */
  37. public function testSortNumbersIdentity(): void
  38. {
  39. $items = new ArrayObject([3, 5, 1, 2, 4]);
  40. $identity = function ($a) {
  41. return $a;
  42. };
  43. $sorted = new SortIterator($items, $identity);
  44. $expected = range(5, 1);
  45. $this->assertEquals($expected, $sorted->toList());
  46. $sorted = new SortIterator($items, $identity, SORT_ASC);
  47. $expected = range(1, 5);
  48. $this->assertEquals($expected, $sorted->toList());
  49. }
  50. /**
  51. * Tests sorting numbers with custom callback
  52. */
  53. public function testSortNumbersCustom(): void
  54. {
  55. $items = new ArrayObject([3, 5, 1, 2, 4]);
  56. $callback = function ($a) {
  57. return $a * -1;
  58. };
  59. $sorted = new SortIterator($items, $callback);
  60. $expected = range(1, 5);
  61. $this->assertEquals($expected, $sorted->toList());
  62. $sorted = new SortIterator($items, $callback, SORT_ASC);
  63. $expected = range(5, 1);
  64. $this->assertEquals($expected, $sorted->toList());
  65. }
  66. /**
  67. * Tests sorting a complex structure with numeric sort
  68. */
  69. public function testSortComplexNumeric(): void
  70. {
  71. $items = new ArrayObject([
  72. ['foo' => 1, 'bar' => 'a'],
  73. ['foo' => 10, 'bar' => 'a'],
  74. ['foo' => 2, 'bar' => 'a'],
  75. ['foo' => 13, 'bar' => 'a'],
  76. ]);
  77. $callback = function ($a) {
  78. return $a['foo'];
  79. };
  80. $sorted = new SortIterator($items, $callback, SORT_DESC, SORT_NUMERIC);
  81. $expected = [
  82. ['foo' => 13, 'bar' => 'a'],
  83. ['foo' => 10, 'bar' => 'a'],
  84. ['foo' => 2, 'bar' => 'a'],
  85. ['foo' => 1, 'bar' => 'a'],
  86. ];
  87. $this->assertEquals($expected, $sorted->toList());
  88. $sorted = new SortIterator($items, $callback, SORT_ASC, SORT_NUMERIC);
  89. $expected = [
  90. ['foo' => 1, 'bar' => 'a'],
  91. ['foo' => 2, 'bar' => 'a'],
  92. ['foo' => 10, 'bar' => 'a'],
  93. ['foo' => 13, 'bar' => 'a'],
  94. ];
  95. $this->assertEquals($expected, $sorted->toList());
  96. }
  97. /**
  98. * Tests sorting a complex structure with natural sort
  99. */
  100. public function testSortComplexNatural(): void
  101. {
  102. $items = new ArrayObject([
  103. ['foo' => 'foo_1', 'bar' => 'a'],
  104. ['foo' => 'foo_10', 'bar' => 'a'],
  105. ['foo' => 'foo_2', 'bar' => 'a'],
  106. ['foo' => 'foo_13', 'bar' => 'a'],
  107. ]);
  108. $callback = function ($a) {
  109. return $a['foo'];
  110. };
  111. $sorted = new SortIterator($items, $callback, SORT_DESC, SORT_NATURAL);
  112. $expected = [
  113. ['foo' => 'foo_13', 'bar' => 'a'],
  114. ['foo' => 'foo_10', 'bar' => 'a'],
  115. ['foo' => 'foo_2', 'bar' => 'a'],
  116. ['foo' => 'foo_1', 'bar' => 'a'],
  117. ];
  118. $this->assertEquals($expected, $sorted->toList());
  119. $sorted = new SortIterator($items, $callback, SORT_ASC, SORT_NATURAL);
  120. $expected = [
  121. ['foo' => 'foo_1', 'bar' => 'a'],
  122. ['foo' => 'foo_2', 'bar' => 'a'],
  123. ['foo' => 'foo_10', 'bar' => 'a'],
  124. ['foo' => 'foo_13', 'bar' => 'a'],
  125. ];
  126. $this->assertEquals($expected, $sorted->toList());
  127. $this->assertEquals($expected, $sorted->toList(), 'Iterator should rewind');
  128. }
  129. /**
  130. * Tests sorting a complex structure with natural sort with string callback
  131. */
  132. public function testSortComplexNaturalWithPath(): void
  133. {
  134. $items = new ArrayObject([
  135. ['foo' => 'foo_1', 'bar' => 'a'],
  136. ['foo' => 'foo_10', 'bar' => 'a'],
  137. ['foo' => 'foo_2', 'bar' => 'a'],
  138. ['foo' => 'foo_13', 'bar' => 'a'],
  139. ]);
  140. $sorted = new SortIterator($items, 'foo', SORT_DESC, SORT_NATURAL);
  141. $expected = [
  142. ['foo' => 'foo_13', 'bar' => 'a'],
  143. ['foo' => 'foo_10', 'bar' => 'a'],
  144. ['foo' => 'foo_2', 'bar' => 'a'],
  145. ['foo' => 'foo_1', 'bar' => 'a'],
  146. ];
  147. $this->assertEquals($expected, $sorted->toList());
  148. $sorted = new SortIterator($items, 'foo', SORT_ASC, SORT_NATURAL);
  149. $expected = [
  150. ['foo' => 'foo_1', 'bar' => 'a'],
  151. ['foo' => 'foo_2', 'bar' => 'a'],
  152. ['foo' => 'foo_10', 'bar' => 'a'],
  153. ['foo' => 'foo_13', 'bar' => 'a'],
  154. ];
  155. $this->assertEquals($expected, $sorted->toList());
  156. $this->assertEquals($expected, $sorted->toList(), 'Iterator should rewind');
  157. }
  158. /**
  159. * Tests sorting a complex structure with a deep path
  160. */
  161. public function testSortComplexDeepPath(): void
  162. {
  163. $items = new ArrayObject([
  164. ['foo' => ['bar' => 1], 'bar' => 'a'],
  165. ['foo' => ['bar' => 12], 'bar' => 'a'],
  166. ['foo' => ['bar' => 10], 'bar' => 'a'],
  167. ['foo' => ['bar' => 2], 'bar' => 'a'],
  168. ]);
  169. $sorted = new SortIterator($items, 'foo.bar', SORT_ASC, SORT_NUMERIC);
  170. $expected = [
  171. ['foo' => ['bar' => 1], 'bar' => 'a'],
  172. ['foo' => ['bar' => 2], 'bar' => 'a'],
  173. ['foo' => ['bar' => 10], 'bar' => 'a'],
  174. ['foo' => ['bar' => 12], 'bar' => 'a'],
  175. ];
  176. $this->assertEquals($expected, $sorted->toList());
  177. }
  178. /**
  179. * Tests sorting datetime
  180. */
  181. public function testSortDateTime(): void
  182. {
  183. $items = new ArrayObject([
  184. new DateTime('2014-07-21'),
  185. new DateTime('2015-06-30'),
  186. new DateTimeImmutable('2013-08-12'),
  187. ]);
  188. $callback = function ($a) {
  189. return $a->add(new DateInterval('P1Y'));
  190. };
  191. $sorted = new SortIterator($items, $callback);
  192. $expected = [
  193. new DateTime('2016-06-30'),
  194. new DateTime('2015-07-21'),
  195. new DateTimeImmutable('2013-08-12'),
  196. ];
  197. $this->assertEquals($expected, $sorted->toList());
  198. $items = new ArrayObject([
  199. new DateTime('2014-07-21'),
  200. new DateTime('2015-06-30'),
  201. new DateTimeImmutable('2013-08-12'),
  202. ]);
  203. $sorted = new SortIterator($items, $callback, SORT_ASC);
  204. $expected = [
  205. new DateTimeImmutable('2013-08-12'),
  206. new DateTime('2015-07-21'),
  207. new DateTime('2016-06-30'),
  208. ];
  209. $this->assertEquals($expected, $sorted->toList());
  210. }
  211. /**
  212. * Tests sorting with Chronos datetime
  213. */
  214. public function testSortWithChronosDateTime(): void
  215. {
  216. $items = new ArrayObject([
  217. new Chronos('2014-07-21'),
  218. new ChronosDate('2015-06-30'),
  219. new DateTimeImmutable('2013-08-12'),
  220. ]);
  221. $callback = fn ($d) => $d;
  222. $sorted = new SortIterator($items, $callback);
  223. $expected = [
  224. new ChronosDate('2015-06-30'),
  225. new Chronos('2014-07-21'),
  226. new DateTimeImmutable('2013-08-12'),
  227. ];
  228. $this->assertEquals($expected, $sorted->toList());
  229. $items = new ArrayObject([
  230. new Chronos('2014-07-21'),
  231. new ChronosDate('2015-06-30'),
  232. new DateTimeImmutable('2013-08-12'),
  233. ]);
  234. $sorted = new SortIterator($items, $callback, SORT_ASC);
  235. $expected = [
  236. new DateTimeImmutable('2013-08-12'),
  237. new Chronos('2014-07-21'),
  238. new ChronosDate('2015-06-30'),
  239. ];
  240. $this->assertEquals($expected, $sorted->toList());
  241. }
  242. /**
  243. * Tests sorting with Chronos time instances
  244. */
  245. public function testSortWithChronosTime(): void
  246. {
  247. $items = new ArrayObject([
  248. new ChronosTime('12:00:00'),
  249. new ChronosTime('10:00:01'),
  250. new ChronosTime('11:00:00'),
  251. ]);
  252. $callback = fn ($d) => $d;
  253. $sorted = new SortIterator($items, $callback);
  254. $expected = [
  255. new ChronosTime('12:00:00'),
  256. new ChronosTime('11:00:00'),
  257. new ChronosTime('10:00:01'),
  258. ];
  259. $this->assertEquals($expected, $sorted->toList());
  260. $items = new ArrayObject([
  261. new ChronosTime('12:00:00'),
  262. new ChronosTime('10:00:01'),
  263. new ChronosTime('11:00:00'),
  264. ]);
  265. $sorted = new SortIterator($items, $callback, SORT_ASC);
  266. $expected = [
  267. new ChronosTime('10:00:01'),
  268. new ChronosTime('11:00:00'),
  269. new ChronosTime('12:00:00'),
  270. ];
  271. $this->assertEquals($expected, $sorted->toList());
  272. }
  273. }