ConstraintTest.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. <?php
  2. /*
  3. * This file is part of composer/semver.
  4. *
  5. * (c) Composer <https://github.com/composer>
  6. *
  7. * For the full copyright and license information, please view
  8. * the LICENSE file that was distributed with this source code.
  9. */
  10. namespace Composer\Semver\Constraint;
  11. use PHPUnit\Framework\TestCase;
  12. use Composer\Semver\Intervals;
  13. class ConstraintTest extends TestCase
  14. {
  15. /**
  16. * @var Constraint
  17. */
  18. protected $constraint;
  19. /**
  20. * @var Constraint
  21. */
  22. protected $versionProvide;
  23. protected function setUp()
  24. {
  25. $this->constraint = new Constraint('==', '1');
  26. $this->versionProvide = new Constraint('==', 'dev-foo');
  27. }
  28. public function testVersionCompareInvalidArgumentException()
  29. {
  30. $this->doExpectException('InvalidArgumentException');
  31. /** @phpstan-ignore-next-line */
  32. $result = $this->constraint->versionCompare('1.1', '1.2', '!==');
  33. }
  34. public function testGetPrettyString()
  35. {
  36. $expectedString = 'pretty-string';
  37. $this->constraint->setPrettyString($expectedString);
  38. $result = $this->constraint->getPrettyString();
  39. $this->assertSame($expectedString, $result);
  40. $expectedVersion = '== 1';
  41. $this->constraint->setPrettyString(null);
  42. $result = $this->constraint->getPrettyString();
  43. $this->assertSame($expectedVersion, $result);
  44. }
  45. /**
  46. * @return array<mixed>
  47. */
  48. public static function successfulVersionMatches()
  49. {
  50. return array(
  51. // require provide
  52. array('==', '2', '==', '2'),
  53. array('==', '2', '<', '3'),
  54. array('==', '2', '<=', '2'),
  55. array('==', '2', '<=', '3'),
  56. array('==', '2', '>=', '1'),
  57. array('==', '2', '>=', '2'),
  58. array('==', '2', '>', '1'),
  59. array('==', '2', '!=', '1'),
  60. array('==', '2', '!=', '3'),
  61. array('<', '2', '==', '1'),
  62. array('<', '2', '<', '1'),
  63. array('<', '2', '<', '2'),
  64. array('<', '2', '<', '3'),
  65. array('<', '2', '<=', '1'),
  66. array('<', '2', '<=', '2'),
  67. array('<', '2', '<=', '3'),
  68. array('<', '2', '>=', '1'),
  69. array('<', '2', '>', '1'),
  70. array('<', '2', '!=', '1'),
  71. array('<', '2', '!=', '2'),
  72. array('<', '2', '!=', '3'),
  73. array('<=', '2', '==', '1'),
  74. array('<=', '2', '==', '2'),
  75. array('<=', '2', '<', '1'),
  76. array('<=', '2', '<', '2'),
  77. array('<=', '2', '<', '3'),
  78. array('<=', '2', '<=', '1'),
  79. array('<=', '2', '<=', '2'),
  80. array('<=', '2', '<=', '3'),
  81. array('<=', '2', '>=', '1'),
  82. array('<=', '2', '>=', '2'),
  83. array('<=', '2', '>', '1'),
  84. array('<=', '2', '!=', '1'),
  85. array('<=', '2', '!=', '2'),
  86. array('<=', '2', '!=', '3'),
  87. array('>=', '2', '==', '2'),
  88. array('>=', '2', '==', '3'),
  89. array('>=', '2', '<', '3'),
  90. array('>=', '2', '<=', '2'),
  91. array('>=', '2', '<=', '3'),
  92. array('>=', '2', '>=', '1'),
  93. array('>=', '2', '>=', '2'),
  94. array('>=', '2', '>=', '3'),
  95. array('>=', '2', '>', '1'),
  96. array('>=', '2', '>', '2'),
  97. array('>=', '2', '>', '3'),
  98. array('>=', '2', '!=', '1'),
  99. array('>=', '2', '!=', '2'),
  100. array('>=', '2', '!=', '3'),
  101. array('>', '2', '==', '3'),
  102. array('>', '2', '<', '3'),
  103. array('>', '2', '<=', '3'),
  104. array('>', '2', '>=', '1'),
  105. array('>', '2', '>=', '2'),
  106. array('>', '2', '>=', '3'),
  107. array('>', '2', '>', '1'),
  108. array('>', '2', '>', '2'),
  109. array('>', '2', '>', '3'),
  110. array('>', '2', '!=', '1'),
  111. array('>', '2', '!=', '2'),
  112. array('>', '2', '!=', '3'),
  113. array('!=', '2', '!=', '1'),
  114. array('!=', '2', '!=', '2'),
  115. array('!=', '2', '!=', '3'),
  116. array('!=', '2', '==', '1'),
  117. array('!=', '2', '==', '3'),
  118. array('!=', '2', '<', '1'),
  119. array('!=', '2', '<', '2'),
  120. array('!=', '2', '<', '3'),
  121. array('!=', '2', '<=', '1'),
  122. array('!=', '2', '<=', '2'),
  123. array('!=', '2', '<=', '3'),
  124. array('!=', '2', '>=', '1'),
  125. array('!=', '2', '>=', '2'),
  126. array('!=', '2', '>=', '3'),
  127. array('!=', '2', '>', '1'),
  128. array('!=', '2', '>', '2'),
  129. array('!=', '2', '>', '3'),
  130. // branch names
  131. array('==', 'dev-foo-bar', '==', 'dev-foo-bar'),
  132. array('==', 'dev-events+issue-17', '==', 'dev-events+issue-17'),
  133. array('==', 'dev-foo-bar', '!=', 'dev-foo-xyz'),
  134. array('!=', 'dev-foo-bar', '!=', 'dev-foo-xyz'),
  135. // numbers vs branches
  136. array('==', '0.12', '!=', 'dev-foo'),
  137. array('<', '0.12', '!=', 'dev-foo'),
  138. array('<=', '0.12', '!=', 'dev-foo'),
  139. array('>=', '0.12', '!=', 'dev-foo'),
  140. array('>', '0.12', '!=', 'dev-foo'),
  141. array('!=', '0.12', '==', 'dev-foo'),
  142. array('!=', '0.12', '!=', 'dev-foo'),
  143. );
  144. }
  145. /**
  146. * @dataProvider successfulVersionMatches
  147. * @param Constraint::STR_OP_* $requireOperator
  148. * @param string $requireVersion
  149. * @param Constraint::STR_OP_* $provideOperator
  150. * @param string $provideVersion
  151. */
  152. public function testVersionMatchSucceeds($requireOperator, $requireVersion, $provideOperator, $provideVersion)
  153. {
  154. $versionRequire = new Constraint($requireOperator, $requireVersion);
  155. $versionProvide = new Constraint($provideOperator, $provideVersion);
  156. $this->assertTrue($versionRequire->matches($versionProvide));
  157. $this->assertTrue($this->matchCompiled($versionRequire, $provideOperator, $provideVersion));
  158. $this->assertTrue(Intervals::haveIntersections($versionRequire, $versionProvide));
  159. $this->assertTrue(Intervals::compactConstraint($versionRequire)->matches(Intervals::compactConstraint($versionProvide)));
  160. // the operation should be commutative
  161. $this->assertTrue($versionProvide->matches($versionRequire));
  162. $this->assertTrue($this->matchCompiled($versionProvide, $requireOperator, $requireVersion));
  163. $this->assertTrue(Intervals::haveIntersections($versionProvide, $versionRequire));
  164. $this->assertTrue(Intervals::compactConstraint($versionProvide)->matches(Intervals::compactConstraint($versionRequire)));
  165. }
  166. /**
  167. * @return array<mixed>
  168. */
  169. public static function failingVersionMatches()
  170. {
  171. return array(
  172. // require provide
  173. array('==', '2', '==', '1'),
  174. array('==', '2', '==', '3'),
  175. array('==', '2', '<', '1'),
  176. array('==', '2', '<', '2'),
  177. array('==', '2', '<=', '1'),
  178. array('==', '2', '>=', '3'),
  179. array('==', '2', '>', '2'),
  180. array('==', '2', '>', '3'),
  181. array('==', '2', '!=', '2'),
  182. array('<', '2', '==', '2'),
  183. array('<', '2', '==', '3'),
  184. array('<', '2', '>=', '2'),
  185. array('<', '2', '>=', '3'),
  186. array('<', '2', '>', '2'),
  187. array('<', '2', '>', '3'),
  188. array('<=', '2', '==', '3'),
  189. array('<=', '2', '>=', '3'),
  190. array('<=', '2', '>', '2'),
  191. array('<=', '2', '>', '3'),
  192. array('>=', '2', '==', '1'),
  193. array('>=', '2', '<', '1'),
  194. array('>=', '2', '<', '2'),
  195. array('>=', '2', '<=', '1'),
  196. array('>', '2', '==', '1'),
  197. array('>', '2', '==', '2'),
  198. array('>', '2', '<', '1'),
  199. array('>', '2', '<', '2'),
  200. array('>', '2', '<=', '1'),
  201. array('>', '2', '<=', '2'),
  202. array('!=', '2', '==', '2'),
  203. array('==', '2.0-b2', '<', '2.0-beta2'),
  204. array('==', 'dev-foo-dist', '==', 'dev-foo-zist'),
  205. // different branch names
  206. array('==', 'dev-foo-bar', '==', 'dev-foo-xyz'),
  207. array('==', 'dev-foo-bar', '<', 'dev-foo-xyz'),
  208. array('==', 'dev-foo-bar', '<=', 'dev-foo-xyz'),
  209. array('==', 'dev-foo-bar', '>=', 'dev-foo-xyz'),
  210. array('==', 'dev-foo-bar', '>', 'dev-foo-xyz'),
  211. array('<', 'dev-foo-bar', '==', 'dev-foo-xyz'),
  212. array('<', 'dev-foo-bar', '<', 'dev-foo-xyz'),
  213. array('<', 'dev-foo-bar', '<=', 'dev-foo-xyz'),
  214. array('<', 'dev-foo-bar', '>=', 'dev-foo-xyz'),
  215. array('<', 'dev-foo-bar', '>', 'dev-foo-xyz'),
  216. array('<', 'dev-foo-bar', '!=', 'dev-foo-xyz'),
  217. array('<=', 'dev-foo-bar', '==', 'dev-foo-xyz'),
  218. array('<=', 'dev-foo-bar', '<', 'dev-foo-xyz'),
  219. array('<=', 'dev-foo-bar', '<=', 'dev-foo-xyz'),
  220. array('<=', 'dev-foo-bar', '>=', 'dev-foo-xyz'),
  221. array('<=', 'dev-foo-bar', '>', 'dev-foo-xyz'),
  222. array('<=', 'dev-foo-bar', '!=', 'dev-foo-xyz'),
  223. array('>=', 'dev-foo-bar', '==', 'dev-foo-xyz'),
  224. array('>=', 'dev-foo-bar', '<', 'dev-foo-xyz'),
  225. array('>=', 'dev-foo-bar', '<=', 'dev-foo-xyz'),
  226. array('>=', 'dev-foo-bar', '>=', 'dev-foo-xyz'),
  227. array('>=', 'dev-foo-bar', '>', 'dev-foo-xyz'),
  228. array('>=', 'dev-foo-bar', '!=', 'dev-foo-xyz'),
  229. array('>', 'dev-foo-bar', '==', 'dev-foo-xyz'),
  230. array('>', 'dev-foo-bar', '<', 'dev-foo-xyz'),
  231. array('>', 'dev-foo-bar', '<=', 'dev-foo-xyz'),
  232. array('>', 'dev-foo-bar', '>=', 'dev-foo-xyz'),
  233. array('>', 'dev-foo-bar', '>', 'dev-foo-xyz'),
  234. array('>', 'dev-foo-bar', '!=', 'dev-foo-xyz'),
  235. // same branch names
  236. array('==', 'dev-foo-bar', '<', 'dev-foo-bar'),
  237. array('==', 'dev-foo-bar', '<=', 'dev-foo-bar'),
  238. array('==', 'dev-foo-bar', '>=', 'dev-foo-bar'),
  239. array('==', 'dev-foo-bar', '>', 'dev-foo-bar'),
  240. array('==', 'dev-foo-bar', '!=', 'dev-foo-bar'),
  241. array('<', 'dev-foo-bar', '==', 'dev-foo-bar'),
  242. array('<', 'dev-foo-bar', '<', 'dev-foo-bar'),
  243. array('<', 'dev-foo-bar', '<=', 'dev-foo-bar'),
  244. array('<', 'dev-foo-bar', '>=', 'dev-foo-bar'),
  245. array('<', 'dev-foo-bar', '>', 'dev-foo-bar'),
  246. array('<', 'dev-foo-bar', '!=', 'dev-foo-bar'),
  247. array('<=', 'dev-foo-bar', '==', 'dev-foo-bar'),
  248. array('<=', 'dev-foo-bar', '<', 'dev-foo-bar'),
  249. array('<=', 'dev-foo-bar', '<=', 'dev-foo-bar'),
  250. array('<=', 'dev-foo-bar', '>=', 'dev-foo-bar'),
  251. array('<=', 'dev-foo-bar', '>', 'dev-foo-bar'),
  252. array('<=', 'dev-foo-bar', '!=', 'dev-foo-bar'),
  253. array('>=', 'dev-foo-bar', '==', 'dev-foo-bar'),
  254. array('>=', 'dev-foo-bar', '<', 'dev-foo-bar'),
  255. array('>=', 'dev-foo-bar', '<=', 'dev-foo-bar'),
  256. array('>=', 'dev-foo-bar', '>=', 'dev-foo-bar'),
  257. array('>=', 'dev-foo-bar', '>', 'dev-foo-bar'),
  258. array('>=', 'dev-foo-bar', '!=', 'dev-foo-bar'),
  259. array('>', 'dev-foo-bar', '==', 'dev-foo-bar'),
  260. array('>', 'dev-foo-bar', '<', 'dev-foo-bar'),
  261. array('>', 'dev-foo-bar', '<=', 'dev-foo-bar'),
  262. array('>', 'dev-foo-bar', '>=', 'dev-foo-bar'),
  263. array('>', 'dev-foo-bar', '>', 'dev-foo-bar'),
  264. array('>', 'dev-foo-bar', '!=', 'dev-foo-bar'),
  265. // branch vs number, not comparable so mostly false
  266. array('==', '0.12', '==', 'dev-foo'),
  267. array('==', '0.12', '<', 'dev-foo'),
  268. array('==', '0.12', '<=', 'dev-foo'),
  269. array('==', '0.12', '>=', 'dev-foo'),
  270. array('==', '0.12', '>', 'dev-foo'),
  271. array('<', '0.12', '==', 'dev-foo'),
  272. array('<', '0.12', '<', 'dev-foo'),
  273. array('<', '0.12', '<=', 'dev-foo'),
  274. array('<', '0.12', '>=', 'dev-foo'),
  275. array('<', '0.12', '>', 'dev-foo'),
  276. array('<=', '0.12', '==', 'dev-foo'),
  277. array('<=', '0.12', '<', 'dev-foo'),
  278. array('<=', '0.12', '<=', 'dev-foo'),
  279. array('<=', '0.12', '>=', 'dev-foo'),
  280. array('<=', '0.12', '>', 'dev-foo'),
  281. array('>=', '0.12', '==', 'dev-foo'),
  282. array('>=', '0.12', '<', 'dev-foo'),
  283. array('>=', '0.12', '<=', 'dev-foo'),
  284. array('>=', '0.12', '>=', 'dev-foo'),
  285. array('>=', '0.12', '>', 'dev-foo'),
  286. array('>', '0.12', '==', 'dev-foo'),
  287. array('>', '0.12', '<', 'dev-foo'),
  288. array('>', '0.12', '<=', 'dev-foo'),
  289. array('>', '0.12', '>=', 'dev-foo'),
  290. array('>', '0.12', '>', 'dev-foo'),
  291. array('!=', '0.12', '<', 'dev-foo'),
  292. array('!=', '0.12', '<=', 'dev-foo'),
  293. array('!=', '0.12', '>=', 'dev-foo'),
  294. array('!=', '0.12', '>', 'dev-foo'),
  295. );
  296. }
  297. /**
  298. * @dataProvider failingVersionMatches
  299. * @param Constraint::STR_OP_* $requireOperator
  300. * @param string $requireVersion
  301. * @param Constraint::STR_OP_* $provideOperator
  302. * @param string $provideVersion
  303. */
  304. public function testVersionMatchFails($requireOperator, $requireVersion, $provideOperator, $provideVersion)
  305. {
  306. $versionRequire = new Constraint($requireOperator, $requireVersion);
  307. $versionProvide = new Constraint($provideOperator, $provideVersion);
  308. $this->assertFalse($versionRequire->matches($versionProvide));
  309. $this->assertFalse($this->matchCompiled($versionRequire, $provideOperator, $provideVersion));
  310. $this->assertFalse(Intervals::compactConstraint($versionRequire)->matches(Intervals::compactConstraint($versionProvide)));
  311. // the operation should be commutative
  312. $this->assertFalse($versionProvide->matches($versionRequire));
  313. $this->assertFalse($this->matchCompiled($versionProvide, $requireOperator, $requireVersion));
  314. $this->assertFalse(Intervals::compactConstraint($versionProvide)->matches(Intervals::compactConstraint($versionRequire)));
  315. // do not test intersections with >/</>=/<= for dev versions as these are not supported
  316. if (substr($requireVersion, 0, 4) === 'dev-' && $requireOperator !== '==' && $requireOperator !== '!=') {
  317. return;
  318. }
  319. if (substr($provideVersion, 0, 4) === 'dev-' && $provideOperator !== '==' && $provideOperator !== '!=') {
  320. return;
  321. }
  322. $this->assertFalse(Intervals::haveIntersections($versionRequire, $versionProvide));
  323. $this->assertFalse(Intervals::haveIntersections($versionProvide, $versionRequire));
  324. }
  325. public function testInverseMatchingOtherConstraints()
  326. {
  327. $constraint = new Constraint('>', '1.0.0');
  328. $multiConstraint = $this
  329. ->getMockBuilder('Composer\Semver\Constraint\MultiConstraint')
  330. ->disableOriginalConstructor()
  331. ->setMethods(array('matches'))
  332. ->getMock()
  333. ;
  334. $matchAllConstraint = $this
  335. ->getMockBuilder('Composer\Semver\Constraint\MatchAllConstraint')
  336. ->setMethods(array('matches'))
  337. ->getMock()
  338. ;
  339. foreach (array($multiConstraint, $matchAllConstraint) as $mock) {
  340. $mock
  341. ->expects($this->once())
  342. ->method('matches')
  343. ->with($constraint)
  344. ->willReturn(true)
  345. ;
  346. }
  347. // @phpstan-ignore-next-line
  348. $this->assertTrue($constraint->matches($multiConstraint));
  349. // @phpstan-ignore-next-line
  350. $this->assertTrue($constraint->matches($matchAllConstraint));
  351. }
  352. public function testComparableBranches()
  353. {
  354. $versionRequire = new Constraint('>', '0.12');
  355. $this->assertFalse($versionRequire->matches($this->versionProvide));
  356. $this->assertFalse($this->matchCompiled($versionRequire, '==', 'dev-foo'));
  357. $this->assertFalse(Intervals::haveIntersections($versionRequire, $this->versionProvide));
  358. $this->assertFalse(Intervals::compactConstraint($versionRequire)->matches(Intervals::compactConstraint($this->versionProvide)));
  359. $this->assertFalse($versionRequire->matchSpecific($this->versionProvide, true));
  360. $versionRequire = new Constraint('<', '0.12');
  361. $this->assertFalse($versionRequire->matches($this->versionProvide));
  362. $this->assertFalse($this->matchCompiled($versionRequire, '==', 'dev-foo'));
  363. $this->assertFalse(Intervals::haveIntersections($versionRequire, $this->versionProvide));
  364. $this->assertFalse(Intervals::compactConstraint($versionRequire)->matches(Intervals::compactConstraint($this->versionProvide)));
  365. $this->assertTrue($versionRequire->matchSpecific($this->versionProvide, true));
  366. }
  367. /**
  368. * @dataProvider invalidOperators
  369. *
  370. * @param string $version
  371. * @param Constraint::STR_OP_* $operator
  372. * @param class-string $expected
  373. */
  374. public function testInvalidOperators($version, $operator, $expected)
  375. {
  376. $this->doExpectException($expected);
  377. new Constraint($operator, $version);
  378. }
  379. /**
  380. * @return array<mixed>
  381. */
  382. public function invalidOperators()
  383. {
  384. return array(
  385. array('1.2.3', 'invalid', 'InvalidArgumentException'),
  386. array('1.2.3', '!', 'InvalidArgumentException'),
  387. array('1.2.3', 'equals', 'InvalidArgumentException'),
  388. );
  389. }
  390. /**
  391. * @dataProvider bounds
  392. *
  393. * @param Constraint::STR_OP_* $operator
  394. * @param string $normalizedVersion
  395. * @param Bound $expectedLower
  396. * @param Bound $expectedUpper
  397. */
  398. public function testBounds($operator, $normalizedVersion, Bound $expectedLower, Bound $expectedUpper)
  399. {
  400. $constraint = new Constraint($operator, $normalizedVersion);
  401. $this->assertEquals($expectedLower, $constraint->getLowerBound(), 'Expected lower bound does not match');
  402. $this->assertEquals($expectedUpper, $constraint->getUpperBound(), 'Expected upper bound does not match');
  403. }
  404. /**
  405. * @return array<mixed>
  406. */
  407. public function bounds()
  408. {
  409. return array(
  410. 'equal to 1.0.0.0' => array('==', '1.0.0.0', new Bound('1.0.0.0', true), new Bound('1.0.0.0', true)),
  411. 'equal to 1.0.0.0-rc3' => array('==', '1.0.0.0-rc3', new Bound('1.0.0.0-rc3', true), new Bound('1.0.0.0-rc3', true)),
  412. 'equal to dev-feature-branch' => array('>=', 'dev-feature-branch', Bound::zero(), Bound::positiveInfinity()),
  413. 'lower than 0.0.4.0' => array('<', '0.0.4.0', Bound::zero(), new Bound('0.0.4.0', false)),
  414. 'lower than 1.0.0.0' => array('<', '1.0.0.0', Bound::zero(), new Bound('1.0.0.0', false)),
  415. 'lower than 2.0.0.0' => array('<', '2.0.0.0', Bound::zero(), new Bound('2.0.0.0', false)),
  416. 'lower than 3.0.3.0' => array('<', '3.0.3.0', Bound::zero(), new Bound('3.0.3.0', false)),
  417. 'lower than 3.0.3.0-rc3' => array('<', '3.0.3.0-rc3', Bound::zero(), new Bound('3.0.3.0-rc3', false)),
  418. 'lower than dev-feature-branch' => array('<', 'dev-feature-branch', Bound::zero(), Bound::positiveInfinity()),
  419. 'greater than 0.0.4.0' => array('>', '0.0.4.0', new Bound('0.0.4.0', false), Bound::positiveInfinity()),
  420. 'greater than 1.0.0.0' => array('>', '1.0.0.0', new Bound('1.0.0.0', false), Bound::positiveInfinity()),
  421. 'greater than 2.0.0.0' => array('>', '2.0.0.0', new Bound('2.0.0.0', false), Bound::positiveInfinity()),
  422. 'greater than 3.0.3.0' => array('>', '3.0.3.0', new Bound('3.0.3.0', false), Bound::positiveInfinity()),
  423. 'greater than 3.0.3.0-rc3' => array('>', '3.0.3.0-rc3', new Bound('3.0.3.0-rc3', false), Bound::positiveInfinity()),
  424. 'greater than dev-feature-branch' => array('>', 'dev-feature-branch', Bound::zero(), Bound::positiveInfinity()),
  425. 'lower than or equal to 0.0.4.0' => array('<=', '0.0.4.0', Bound::zero(), new Bound('0.0.4.0', true)),
  426. 'lower than or equal to 1.0.0.0' => array('<=', '1.0.0.0', Bound::zero(), new Bound('1.0.0.0', true)),
  427. 'lower than or equal to 2.0.0.0' => array('<=', '2.0.0.0', Bound::zero(), new Bound('2.0.0.0', true)),
  428. 'lower than or equal to 3.0.3.0' => array('<=', '3.0.3.0', Bound::zero(), new Bound('3.0.3.0', true)),
  429. 'lower than or equal to 3.0.3.0-rc3' => array('<=', '3.0.3.0-rc3', Bound::zero(), new Bound('3.0.3.0-rc3', true)),
  430. 'lower than or equal to dev-feature-branch' => array('<=', 'dev-feature-branch', Bound::zero(), Bound::positiveInfinity()),
  431. 'greater than or equal to 0.0.4.0' => array('>=', '0.0.4.0', new Bound('0.0.4.0', true), Bound::positiveInfinity()),
  432. 'greater than or equal to 1.0.0.0' => array('>=', '1.0.0.0', new Bound('1.0.0.0', true), Bound::positiveInfinity()),
  433. 'greater than or equal to 2.0.0.0' => array('>=', '2.0.0.0', new Bound('2.0.0.0', true), Bound::positiveInfinity()),
  434. 'greater than or equal to 3.0.3.0' => array('>=', '3.0.3.0', new Bound('3.0.3.0', true), Bound::positiveInfinity()),
  435. 'greater than or equal to 3.0.3.0-rc3' => array('>=', '3.0.3.0-rc3', new Bound('3.0.3.0-rc3', true), Bound::positiveInfinity()),
  436. 'greater than or equal to dev-feature-branch' => array('>=', 'dev-feature-branch', Bound::zero(), Bound::positiveInfinity()),
  437. 'not equal to 1.0.0.0' => array('<>', '1.0.0.0', Bound::zero(), Bound::positiveInfinity()),
  438. );
  439. }
  440. /**
  441. * @dataProvider matrix
  442. * @param Constraint::STR_OP_* $requireOperator
  443. * @param string $requireVersion
  444. * @param Constraint::STR_OP_* $provideOperator
  445. * @param string $provideVersion
  446. */
  447. public function testCompile($requireOperator, $requireVersion, $provideOperator, $provideVersion)
  448. {
  449. $require = new Constraint($requireOperator, $requireVersion);
  450. $provide = new Constraint($provideOperator, $provideVersion);
  451. // Asserts Compiled version returns the same result than standard
  452. $this->assertSame($m = $require->matches($provide), $this->matchCompiled($require, $provideOperator, $provideVersion));
  453. $this->assertSame($m, Intervals::compactConstraint($require)->matches(Intervals::compactConstraint($provide)));
  454. $this->assertSame($m, $this->matchCompiled(Intervals::compactConstraint($require), $provideOperator, $provideVersion));
  455. // do not test >/</>=/<= for dev versions as these are not supported
  456. if (substr($requireVersion, 0, 4) === 'dev-' && $requireOperator !== '==' && $requireOperator !== '!=') {
  457. return;
  458. }
  459. if (substr($provideVersion, 0, 4) === 'dev-' && $provideOperator !== '==' && $provideOperator !== '!=') {
  460. return;
  461. }
  462. $this->assertSame($m, Intervals::haveIntersections($require, $provide));
  463. }
  464. /**
  465. * @return array<mixed>
  466. */
  467. public function matrix()
  468. {
  469. $versions = array('1.0', '2.0', 'dev-master', 'dev-foo', '3.0-b2', '3.0-beta2');
  470. $operators = array('==', '!=', '>', '<', '>=', '<=');
  471. $matrix = array();
  472. foreach ($versions as $requireVersion) {
  473. foreach ($operators as $requireOperator) {
  474. foreach ($versions as $provideVersion) {
  475. foreach ($operators as $provideOperator) {
  476. $matrix[] = array($requireOperator, $requireVersion, $provideOperator, $provideVersion);
  477. }
  478. }
  479. }
  480. }
  481. return $matrix;
  482. }
  483. /**
  484. * @param Constraint::STR_OP_* $operator
  485. * @param string $version
  486. * @return bool
  487. */
  488. private function matchCompiled(ConstraintInterface $constraint, $operator, $version)
  489. {
  490. $map = array(
  491. '=' => Constraint::OP_EQ,
  492. '==' => Constraint::OP_EQ,
  493. '<' => Constraint::OP_LT,
  494. '<=' => Constraint::OP_LE,
  495. '>' => Constraint::OP_GT,
  496. '>=' => Constraint::OP_GE,
  497. '<>' => Constraint::OP_NE,
  498. '!=' => Constraint::OP_NE,
  499. );
  500. $code = $constraint->compile($map[$operator]);
  501. $v = $version;
  502. $b = 'dev-' === substr($v, 0, 4);
  503. return eval("return $code;");
  504. }
  505. /**
  506. * @param class-string $class
  507. * @return void
  508. */
  509. private function doExpectException($class)
  510. {
  511. if (method_exists($this, 'expectException')) {
  512. $this->expectException($class);
  513. } else {
  514. // @phpstan-ignore-next-line
  515. $this->setExpectedException($class);
  516. }
  517. }
  518. }