QueryTest.php 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4. * This file is part of the league/commonmark package.
  5. *
  6. * (c) Colin O'Dell <colinodell@gmail.com>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. namespace League\CommonMark\Tests\Unit\Node;
  12. use League\CommonMark\Node\Node;
  13. use League\CommonMark\Node\Query;
  14. use PHPUnit\Framework\TestCase;
  15. final class QueryTest extends TestCase
  16. {
  17. public function testFindOne(): void
  18. {
  19. $result = (new Query())
  20. ->where(static function (Node $node): bool {
  21. return $node->data->get('number') === 7;
  22. })
  23. ->findOne($this->createAST());
  24. $this->assertNotNull($result);
  25. $this->assertSame(7, $result->data->get('number'));
  26. }
  27. public function testFindOneWhenNothingMatches(): void
  28. {
  29. $result = (new Query())
  30. ->where(Query::hasParent())
  31. ->findOne(new SimpleNode());
  32. $this->assertNull($result);
  33. }
  34. public function testFindOneWithNoCriteria(): void
  35. {
  36. $result = (new Query())
  37. ->findOne($this->createAST());
  38. $this->assertNotNull($result);
  39. $this->assertSame(1, $result->data->get('number'));
  40. }
  41. public function testFindAll(): void
  42. {
  43. $result = (new Query())
  44. ->where(Query::hasParent())
  45. ->findAll($this->createAST());
  46. $result = \iterator_to_array($result);
  47. $this->assertCount(6, $result);
  48. // The order is based on node walking
  49. $this->assertSame(2, $result[0]->data->get('number'));
  50. $this->assertSame(3, $result[1]->data->get('number'));
  51. $this->assertSame(6, $result[2]->data->get('number'));
  52. $this->assertSame(4, $result[3]->data->get('number'));
  53. $this->assertSame(7, $result[4]->data->get('number'));
  54. $this->assertSame(5, $result[5]->data->get('number'));
  55. }
  56. public function testFindAllWithLimit(): void
  57. {
  58. $result = (new Query())
  59. ->where(Query::hasParent())
  60. ->findAll($this->createAST(), 3);
  61. $result = \iterator_to_array($result);
  62. $this->assertCount(3, $result);
  63. $this->assertSame(2, $result[0]->data->get('number'));
  64. $this->assertSame(3, $result[1]->data->get('number'));
  65. $this->assertSame(6, $result[2]->data->get('number'));
  66. }
  67. public function testFindAllWithMixedConditions(): void
  68. {
  69. $ast = $this->createAST();
  70. // "class is SimpleNode AND has parent"
  71. $query = (new Query())->where(Query::type(SimpleNode::class), Query::hasParent());
  72. $this->assertCount(6, $query->findAll($ast));
  73. // "class is SimpleNode AND has parent AND number > 2"
  74. $query->andWhere(static function (Node $node): bool {
  75. return $node->data->get('number') > 2;
  76. });
  77. $this->assertCount(5, $query->findAll($ast));
  78. // "class is SimpleNode AND has parent AND number > 2 AND number > 3 AND number > 7"
  79. $query->andWhere(
  80. static function (Node $node): bool {
  81. return $node->data->get('number') > 3;
  82. },
  83. static function (Node $node): bool {
  84. return $node->data->get('number') < 7;
  85. }
  86. );
  87. $this->assertCount(3, $query->findAll($ast));
  88. // "has child OR (class is SimpleNode AND has parent AND number > 2 AND number > 3 AND number > 7)"
  89. $query->orWhere(Query::hasChild());
  90. $this->assertCount(5, $query->findAll($ast));
  91. // "has child OR (class is SimpleNode AND has parent AND number > 2 AND number > 3 AND number > 7) OR has parent OR class is SimpleNode"
  92. $query->orWhere(Query::hasParent(), Query::type(SimpleNode::class));
  93. $this->assertCount(7, $query->findAll($ast));
  94. // "number > 4 AND (has child OR (class is SimpleNode AND has parent AND number > 2 AND number > 3 AND number > 7) OR has parent OR class is SimpleNode)"
  95. $query->andWhere(static function (Node $node): bool {
  96. return $node->data->get('number') > 4;
  97. });
  98. $this->assertCount(3, $query->findAll($ast));
  99. }
  100. public function testWhereUsingExpressionInterface(): void
  101. {
  102. $query = (new Query())->where(new class implements Query\ExpressionInterface {
  103. public function __invoke(Node $node): bool
  104. {
  105. return $node->data->get('number') % 2 === 0;
  106. }
  107. });
  108. $this->assertCount(3, $query->findAll($this->createAST()));
  109. }
  110. public function testWhereClausesUsingExpressionInterface(): void
  111. {
  112. $hasEvenNumber = new class implements Query\ExpressionInterface {
  113. public function __invoke(Node $node): bool
  114. {
  115. return $node->data->get('number') % 2 === 0;
  116. }
  117. };
  118. $greaterThanThree = new class implements Query\ExpressionInterface {
  119. public function __invoke(Node $node): bool
  120. {
  121. return $node->data->get('number') > 3;
  122. }
  123. };
  124. $isNumberFive = new class implements Query\ExpressionInterface {
  125. public function __invoke(Node $node): bool
  126. {
  127. return $node->data->get('number') === 5;
  128. }
  129. };
  130. $result = (new Query())
  131. ->where($hasEvenNumber)
  132. ->orWhere($isNumberFive)
  133. ->andWhere($greaterThanThree)
  134. ->findAll($this->createAST());
  135. $result = \iterator_to_array($result);
  136. $this->assertCount(3, $result);
  137. $this->assertSame(6, $result[0]->data->get('number'));
  138. $this->assertSame(4, $result[1]->data->get('number'));
  139. $this->assertSame(5, $result[2]->data->get('number'));
  140. }
  141. public function testOrWhereUsingExpressionInterface(): void
  142. {
  143. $query = (new Query())
  144. ->where(Query::hasChild())
  145. ->orWhere(
  146. new class implements Query\ExpressionInterface {
  147. public function __invoke(Node $node): bool
  148. {
  149. return $node->data->get('number') % 2 === 0;
  150. }
  151. }
  152. );
  153. $this->assertCount(5, $query->findAll($this->createAST()));
  154. }
  155. public function testFindAllWhenNothingMatches(): void
  156. {
  157. $result = (new Query())
  158. ->where(static function (Node $node): bool {
  159. return false;
  160. })
  161. ->findAll($this->createAST());
  162. $this->assertCount(0, $result);
  163. }
  164. public function testFindAllWithNoCriteria(): void
  165. {
  166. $result = (new Query())->findAll($this->createAST());
  167. $this->assertCount(7, $result);
  168. }
  169. public function testType(): void
  170. {
  171. $test = Query::type(SimpleNode::class);
  172. $this->assertTrue($test(new SimpleNode()));
  173. $this->assertFalse($test($this->createMock(Node::class)));
  174. }
  175. public function testHasChild(): void
  176. {
  177. $test = Query::hasChild();
  178. $parent = new SimpleNode();
  179. $child = new SimpleNode();
  180. $parent->appendChild($child);
  181. $this->assertTrue($test($parent));
  182. $this->assertFalse($test($child));
  183. }
  184. public function testHasChildWithCondition(): void
  185. {
  186. $test = Query::hasChild(static function (Node $node): bool {
  187. return $node->data->has('test');
  188. });
  189. $parent = new SimpleNode();
  190. $child = new SimpleNode();
  191. $parent->appendChild($child);
  192. $this->assertFalse($test($parent));
  193. $this->assertFalse($test($child));
  194. $child->data->set('test', true);
  195. $this->assertTrue($test($parent));
  196. $this->assertFalse($test($child));
  197. }
  198. public function testHasParent(): void
  199. {
  200. $test = Query::hasParent();
  201. $parent = new SimpleNode();
  202. $child = new SimpleNode();
  203. $parent->appendChild($child);
  204. $this->assertFalse($test($parent));
  205. $this->assertTrue($test($child));
  206. }
  207. public function testHasParentWithCondition(): void
  208. {
  209. $test = Query::hasParent(static function (Node $node): bool {
  210. return $node->data->has('test');
  211. });
  212. $parent = new SimpleNode();
  213. $child = new SimpleNode();
  214. $parent->appendChild($child);
  215. $this->assertFalse($test($parent));
  216. $this->assertFalse($test($child));
  217. $parent->data->set('test', true);
  218. $this->assertFalse($test($parent));
  219. $this->assertTrue($test($child));
  220. }
  221. private function createAST(): Node
  222. {
  223. $parent = new SimpleNode();
  224. $parent->data->set('number', 1);
  225. $child1 = new SimpleNode();
  226. $child1->data->set('number', 2);
  227. $parent->appendChild($child1);
  228. $child2 = new SimpleNode();
  229. $child2->data->set('number', 3);
  230. $parent->appendChild($child2);
  231. $child3 = new SimpleNode();
  232. $child3->data->set('number', 4);
  233. $parent->appendChild($child3);
  234. $child4 = new SimpleNode();
  235. $child4->data->set('number', 5);
  236. $parent->appendChild($child4);
  237. $grandchild1 = new SimpleNode();
  238. $grandchild1->data->set('number', 6);
  239. $child2->appendChild($grandchild1);
  240. $grandchild2 = new SimpleNode();
  241. $grandchild2->data->set('number', 7);
  242. $child3->appendChild($grandchild2);
  243. return $parent;
  244. }
  245. }