Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions src/Intervals.php
Original file line number Diff line number Diff line change
Expand Up @@ -150,16 +150,37 @@ public static function compactConstraint(ConstraintInterface $constraint)
$constraints[] = $intervals['numeric'][0]->getStart();
$hasNumericMatchAll = true;
} else {
// A bare != N matches all dev-* versions, so if the numeric part is the whole line minus some points,
// only compact it into unbounded != constraints when the branches already match all dev versions
$numeric = $intervals['numeric'];
$count = \count($numeric);
$skipNotEqual = false;
if (!$intervals['branches']['exclude'] && $count > 1
&& (string) $numeric[0]->getStart() === (string) Interval::fromZero()
&& (string) $numeric[$count - 1]->getEnd() === (string) Interval::untilPositiveInfinity()
) {
$skipNotEqual = true;
for ($i = 0; $i < $count - 1; $i++) {
if ($numeric[$i]->getEnd()->getOperator() !== '<'
|| $numeric[$i + 1]->getStart()->getOperator() !== '>'
|| $numeric[$i]->getEnd()->getVersion() !== $numeric[$i + 1]->getStart()->getVersion()
) {
$skipNotEqual = false;
break;
}
}
}

$unEqualConstraints = array();
for ($i = 0, $count = \count($intervals['numeric']); $i < $count; $i++) {
$interval = $intervals['numeric'][$i];
for ($i = 0; $i < $count; $i++) {
$interval = $numeric[$i];

// if current interval ends with < N and next interval begins with > N we can swap this out for != N
// but this needs to happen as a conjunctive expression together with the start of the current interval
// and end of next interval, so [>=M, <N] || [>N, <P] => [>=M, !=N, <P] but M/P can be skipped if
// they are zero/+inf
if ($interval->getEnd()->getOperator() === '<' && $i+1 < $count) {
$nextInterval = $intervals['numeric'][$i+1];
if (!$skipNotEqual && $interval->getEnd()->getOperator() === '<' && $i+1 < $count) {
$nextInterval = $numeric[$i+1];
if ($interval->getEnd()->getVersion() === $nextInterval->getStart()->getVersion() && $nextInterval->getStart()->getOperator() === '>') {
// only add a start if we didn't already do so, can be skipped if we're looking at second
// interval in [>=M, <N] || [>N, <P] || [>P, <Q] where unEqualConstraints currently contains
Expand Down
52 changes: 52 additions & 0 deletions tests/IntervalsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,43 @@ public function testCompactConstraint($expected, $toCompact, $conjunctive)
$this->assertSame((string) $expected, (string) $new);
}

/**
* @dataProvider compactDevSafetyProvider
* @param array<string> $toCompact
* @param bool $conjunctive
*/
public function testCompactConstraintKeepsMatchedSet($toCompact, $conjunctive)
{
$parser = new VersionParser;

$parts = array();
foreach ($toCompact as $part) {
$parts[] = $parser->parseConstraints($part);
}
$constraint = new MultiConstraint($parts, $conjunctive);

$compacted = Intervals::compactConstraint($constraint);
$dev = new Constraint('==', 'dev-foo');

$this->assertSame($constraint->matches($dev), $compacted->matches($dev), (string) $compacted);
$this->assertTrue(Intervals::isSubsetOf($compacted, $constraint), (string) $compacted);
$this->assertTrue(Intervals::isSubsetOf($constraint, $compacted), (string) $compacted);
}

public static function compactDevSafetyProvider()
{
return array(
'whole line minus one point, no dev' => array(array('> 1.0.0 != 2.0.0', '< 1.9.0'), false),
'whole line minus two points, no dev' => array(array('> 1.0.0 != 2.0.0 != 3.0.0', '< 1.5'), false),
'whole line minus one point, named branch' => array(array('> 1.0.0 != 2.0.0', '< 1.5', 'dev-bar'), false),
'whole line minus two points, named branch' => array(array('> 1.0.0 != 2.0.0 != 3.0.0', '< 1.5', 'dev-bar'), false),
'bounded != group' => array(array('> 1.0.0 != 2.0.0 < 3.0.0', '< 1.5', '>= 5.0'), false),
'conjunctive !=, all dev' => array(array('!= 2.0', '!= 3.0'), true),
'conjunctive != with excluded branch' => array(array('!= 2.0', '!= dev-foo'), true),
'disjunctive != with named branch' => array(array('!= 2.0', 'dev-bar'), false),
);
}

public static function compactProvider()
{
return array(
Expand Down Expand Up @@ -114,6 +151,21 @@ public static function compactProvider()
array('!= 1.0', '!= 1.0', '!= dev-foo', '1.0.5.*'),
false
),
'whole line minus one point without dev does not become a bare !=' => array(
'< 2.0.0.0-stable || > 2.0.0',
array('> 1.0.0 != 2.0.0', '< 1.9.0'),
false
),
'whole line minus two points without dev does not become a bare !=' => array(
'< 2.0.0.0-stable || > 2.0.0 < 3.0.0.0-stable || > 3.0.0',
array('> 1.0.0 != 2.0.0 != 3.0.0', '< 1.5'),
false
),
'whole line minus two points with named branch does not become a bare !=' => array(
'< 2.0.0.0-stable || > 2.0.0 < 3.0.0.0-stable || > 3.0.0 || dev-bar',
array('> 1.0.0 != 2.0.0 != 3.0.0', '< 1.5', 'dev-bar'),
false
),
'conjunctive with complex negation' => array(
'1.0.5.*',
array('!= 1.0', '!= 1.0', '!= dev-foo', '1.0.5.*'),
Expand Down
Loading