Skip to content

Commit

Permalink
Merge pull request #76 from Roave/fix/#66-#72-#73-handle-missing-cli-…
Browse files Browse the repository at this point in the history
…options

#66 #72 #73 fixed missing `--from` handling, minor version detection and scenario with no detected versions at all
  • Loading branch information
Ocramius authored Jun 4, 2018
2 parents 9e95a60 + f3d92aa commit 95b5c0d
Show file tree
Hide file tree
Showing 6 changed files with 364 additions and 26 deletions.
3 changes: 3 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
<testsuite name="unit">
<directory>./test/unit</directory>
</testsuite>
<testsuite name="end to end">
<directory>./test/e2e</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
Expand Down
13 changes: 9 additions & 4 deletions src/Command/AssertBackwardsCompatible.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ public function execute(InputInterface $input, OutputInterface $output) : int
// @todo fix flaky assumption about the path of the source repo...
$sourceRepo = CheckedOutRepository::fromPath(getcwd());

$fromRevision = $input->hasParameterOption('from')
$fromRevision = $input->getOption('from') !== null
? $this->parseRevisionFromInput($input, $sourceRepo)
: $this->determineFromRevisionFromRepository($sourceRepo, $stdErr);

Expand Down Expand Up @@ -226,10 +226,15 @@ private function determineFromRevisionFromRepository(
CheckedOutRepository $repository,
OutputInterface $output
) : Revision {
$versionString = $this->pickFromVersion->forVersions(
$this->getVersions->fromRepository($repository)
)->getVersionString();
$versions = $this->getVersions->fromRepository($repository);

Assert::that($versions->count())
->greaterThan(0, 'Could not detect any released versions for the given repository');

$versionString = $this->pickFromVersion->forVersions($versions)->getVersionString();

$output->writeln(sprintf('Detected last minor version: %s', $versionString));

return $this->parseRevision->fromStringForRepository(
$versionString,
$repository
Expand Down
34 changes: 21 additions & 13 deletions src/Git/PickLastMinorVersionFromCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@

namespace Roave\BackwardCompatibility\Git;

use Assert\Assert;
use Symfony\Component\Process\Exception\LogicException;
use Symfony\Component\Process\Exception\RuntimeException;
use Version\Constraint\ComparisonConstraint;
use Version\Constraint\CompositeConstraint;
use Version\Version;
use Version\VersionsCollection;
use function array_values;
use function iterator_to_array;
use function reset;

final class PickLastMinorVersionFromCollection implements PickVersionFromVersionCollection
{
Expand All @@ -20,23 +23,28 @@ final class PickLastMinorVersionFromCollection implements PickVersionFromVersion
*/
public function forVersions(VersionsCollection $versions) : Version
{
Assert::that($versions->count())
->greaterThan(0, 'Cannot determine latest minor version from an empty collection');

$versions->sort(VersionsCollection::SORT_DESC);

/** @var Version[] $versionsAsArray */
$versionsAsArray = iterator_to_array($versions->getIterator());
/** @var Version $lastVersion */
$lastVersion = reset($versionsAsArray);
$previousVersionInIteration = $lastVersion;
$lastVersion = array_values(iterator_to_array($versions))[0];

$matchingMinorVersions = $versions->matching(new CompositeConstraint(
CompositeConstraint::OPERATOR_AND,
new ComparisonConstraint(ComparisonConstraint::OPERATOR_LTE, $lastVersion),
new ComparisonConstraint(
ComparisonConstraint::OPERATOR_GTE,
Version::fromString($lastVersion->getMajor() . '.' . $lastVersion->getMinor() . '.0')
)
));

/** @var Version $version */
foreach ($versions as $version) {
if ($lastVersion->getMinor() !== $version->getMinor()) {
return $previousVersionInIteration;
}
$matchingMinorVersions->sort(VersionsCollection::SORT_ASC);

$previousVersionInIteration = $version;
}
/** @var Version[] $matchingMinorVersionsAsArray */
$matchingMinorVersionsAsArray = array_values(iterator_to_array($matchingMinorVersions));

return $previousVersionInIteration;
return $matchingMinorVersionsAsArray[0];
}
}
276 changes: 276 additions & 0 deletions test/e2e/Command/AssertBackwardsCompatibleTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
<?php

declare(strict_types=1);

namespace RoaveE2ETest\BackwardCompatibility\Command;

use PHPUnit\Framework\TestCase;
use Symfony\Component\Process\Process;

/**
* @coversNothing
*/
final class AssertBackwardsCompatibleTest extends TestCase
{
private const COMPOSER_MANIFEST = <<<'JSON'
{
"autoload": {
"psr-4": {
"TestArtifact\\": "src/"
}
},
"repositories": [
{
"packagist.org": false
}
]
}

JSON;

private const CLASS_VERSIONS = [
<<<'PHP'
<?php
namespace TestArtifact;
final class TheClass
{
public function method(A $a)
{
}
}

PHP
,
<<<'PHP'
<?php
namespace TestArtifact;
final class TheClass
{
public function method(B $a)
{
}
}

PHP
,
<<<'PHP'
<?php
namespace TestArtifact;
final class TheClass
{
public function method(C $a)
{
}
}

PHP
,
// The last version resets the class to its initial state
<<<'PHP'
<?php
namespace TestArtifact;
final class TheClass
{
public function method(A $a)
{
}
}

PHP
];

/** @var string path to the sources that should be checked */
private $sourcesRepository;

/** @var string[] sha1 of the source versions */
private $versions = [];

protected function setUp() : void
{
parent::setUp();

$this->sourcesRepository = tempnam(sys_get_temp_dir(), 'roave-backward-compatibility-e2e-test');

self::assertInternalType('string', $this->sourcesRepository);
self::assertNotEmpty($this->sourcesRepository);
self::assertFileExists($this->sourcesRepository);

unlink($this->sourcesRepository);
mkdir($this->sourcesRepository);
mkdir($this->sourcesRepository . '/src');

self::assertDirectoryExists($this->sourcesRepository);
self::assertDirectoryExists($this->sourcesRepository . '/src');

(new Process('git init', $this->sourcesRepository))->mustRun();

file_put_contents($this->sourcesRepository . '/composer.json', self::COMPOSER_MANIFEST);

(new Process('git add -A', $this->sourcesRepository))->mustRun();
(new Process('git commit -am "Initial commit with composer manifest"', $this->sourcesRepository))->mustRun();

foreach (self::CLASS_VERSIONS as $key => $classCode) {
file_put_contents($this->sourcesRepository . '/src/TheClass.php', $classCode);

(new Process('git add -A', $this->sourcesRepository))->mustRun();
(new Process(sprintf('git commit -am "Class sources v%d"', $key + 1), $this->sourcesRepository))->mustRun();
$this->versions[$key] = trim((new Process('git rev-parse HEAD', $this->sourcesRepository))->mustRun()
->getOutput());
}
}

protected function tearDown() : void
{
self::assertInternalType('string', $this->sourcesRepository);
self::assertNotEmpty($this->sourcesRepository);
self::assertDirectoryExists($this->sourcesRepository);

// Need to be extremely careful with this stuff - skipping it for now
(new Process(['rm', '-r', $this->sourcesRepository]))->mustRun();

parent::tearDown();
}

public function testWillAllowSpecifyingGitRevision() : void
{
$check = new Process(
[
__DIR__ . '/../../../bin/roave-backward-compatibility-check',
'--from=' . $this->versions[0],
'--to=' . $this->versions[1],
],
$this->sourcesRepository
);

self::assertSame(3, $check->run());
self::assertStringEndsWith(
<<<'EXPECTED'
[BC] CHANGED: The parameter $a of TestArtifact\TheClass#method() changed from TestArtifact\A to a non-contravariant TestArtifact\B
1 backwards-incompatible changes detected

EXPECTED
,
$check->getErrorOutput() // @TODO https://github.com/Roave/BackwardCompatibilityCheck/issues/79 this looks like a symfony bug - we shouldn't check STDERR, but STDOUT
);
}

public function testWillNotRunWithoutTagsNorSpecifiedVersions() : void
{
$check = new Process(
__DIR__ . '/../../../bin/roave-backward-compatibility-check',
$this->sourcesRepository
);

self::assertSame(212, $check->run());
self::assertContains(
'Could not detect any released versions for the given repository',
$check->getErrorOutput()
);
}

public function testWillRunSuccessfullyOnNoBcBreaks() : void
{
$check = new Process(
[
__DIR__ . '/../../../bin/roave-backward-compatibility-check',
'--from=' . $this->versions[0],
'--to=' . $this->versions[3],
],
$this->sourcesRepository
);

self::assertSame(0, $check->run());
self::assertContains(
'No backwards-incompatible changes detected',
$check->getErrorOutput()
);
}

public function testWillPickTaggedVersionOnNoGivenFrom() : void
{
$this->tagOnVersion('1.2.3', 1);

$check = new Process(
[
__DIR__ . '/../../../bin/roave-backward-compatibility-check',
'--to=' . $this->versions[2],
],
$this->sourcesRepository
);

self::assertSame(3, $check->run());

$errorOutput = $check->getErrorOutput();

self::assertContains('Detected last minor version: 1.2.3', $errorOutput);
self::assertStringEndsWith(
<<<'EXPECTED'
[BC] CHANGED: The parameter $a of TestArtifact\TheClass#method() changed from TestArtifact\B to a non-contravariant TestArtifact\C
1 backwards-incompatible changes detected

EXPECTED
,
$errorOutput // @TODO https://github.com/Roave/BackwardCompatibilityCheck/issues/79 this looks like a symfony bug - we shouldn't check STDERR, but STDOUT
);
}

public function testWillPickLatestTaggedVersionOnNoGivenFrom() : void
{
$this->tagOnVersion('2.2.3', 1);
$this->tagOnVersion('1.2.3', 3);

$check = new Process(
[
__DIR__ . '/../../../bin/roave-backward-compatibility-check',
'--to=' . $this->versions[2],
],
$this->sourcesRepository
);

self::assertSame(3, $check->run());

$errorOutput = $check->getErrorOutput();

self::assertContains('Detected last minor version: 2.2.3', $errorOutput);
self::assertStringEndsWith(
<<<'EXPECTED'
[BC] CHANGED: The parameter $a of TestArtifact\TheClass#method() changed from TestArtifact\B to a non-contravariant TestArtifact\C
1 backwards-incompatible changes detected

EXPECTED
,
$errorOutput // @TODO https://github.com/Roave/BackwardCompatibilityCheck/issues/79 this looks like a symfony bug - we shouldn't check STDERR, but STDOUT
);
}

private function tagOnVersion(string $tagName, int $version) : void
{
(new Process(
[
'git',
'checkout',
$this->versions[$version],
],
$this->sourcesRepository
))->mustRun();

(new Process(
[
'git',
'tag',
$tagName,
'-m',
'A tag for version ' . $version,
],
$this->sourcesRepository
))->mustRun();
}
}
Loading

0 comments on commit 95b5c0d

Please sign in to comment.