From 60df1c67eee489ad2a188a6d13172a664571123c Mon Sep 17 00:00:00 2001 From: Ion Bazan Date: Tue, 15 Jun 2021 12:40:07 +0800 Subject: [PATCH 1/3] Add "changed" status, refactor --- .github/workflows/test.yml | 8 +- README.md | 2 + composer.json | 2 +- phpstan.neon | 6 + src/Command/DiffCommand.php | 36 +++--- src/{ => Composer}/Plugin.php | 2 +- src/Diff/DiffEntries.php | 37 ++++++ src/Diff/DiffEntry.php | 111 ++++++++++++++++++ src/Diff/VersionComparator.php | 39 ++++++ src/Formatter/AbstractFormatter.php | 6 +- src/Formatter/Formatter.php | 18 ++- src/Formatter/JsonFormatter.php | 35 +++--- src/Formatter/MarkdownListFormatter.php | 27 ++--- src/Formatter/MarkdownTableFormatter.php | 25 ++-- src/PackageDiff.php | 28 +---- tests/Command/DiffCommandTest.php | 6 +- tests/{ => Composer}/PluginTest.php | 5 +- tests/Diff/DiffEntryTest.php | 87 ++++++++++++++ tests/Formatter/FormatterTest.php | 12 +- tests/Formatter/JsonFormatterTest.php | 12 +- tests/Formatter/MarkdownListFormatterTest.php | 8 +- .../Formatter/MarkdownTableFormatterTest.php | 24 ++-- tests/PackageDiffTest.php | 10 +- tests/TestCase.php | 15 +++ 24 files changed, 423 insertions(+), 138 deletions(-) create mode 100644 phpstan.neon rename src/{ => Composer}/Plugin.php (95%) create mode 100644 src/Diff/DiffEntries.php create mode 100644 src/Diff/DiffEntry.php create mode 100644 src/Diff/VersionComparator.php rename tests/{ => Composer}/PluginTest.php (81%) create mode 100644 tests/Diff/DiffEntryTest.php diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c32abc4..66414be 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,6 +19,7 @@ jobs: - '7.3' - '7.4' - '8.0' + - '8.1' include: - php-versions: '5.3' composer-flags: '--prefer-lowest' @@ -50,15 +51,14 @@ jobs: - name: Upload coverage to Codecov uses: codecov/codecov-action@v1 - name: Run mutation tests - if: ${{ matrix.php-versions == 7.4 && matrix.operating-system == 'ubuntu-latest' }} + if: ${{ matrix.php-versions == 8.0 && matrix.operating-system == 'ubuntu-latest' }} env: STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} run: | composer req infection/infection vendor/bin/infection --ignore-msi-with-no-mutations --min-covered-msi=100 --min-msi=100 -s -j4 - name: Run phpstan - if: ${{ matrix.php-versions >= 7.1 && matrix.php-versions < 8.0 }} + if: ${{ matrix.php-versions == 8.0 && matrix.operating-system == 'ubuntu-latest' }} run: | composer req phpstan/phpstan - vendor/bin/phpstan analyse src -l 6 - + vendor/bin/phpstan diff --git a/README.md b/README.md index abd6bab..46cfda6 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Composer Diff Plugin +[![PHP 5.3+ | 7.x | 8.x](https://img.shields.io/badge/PHP-^5.3_|_^7_|_^8-blue.svg)](https://packagist.org/packages/ion-bazan/composer-diff) +[![Composer v1 | v2](https://img.shields.io/badge/Composer-^1.1_|_^2-success.svg)](https://packagist.org/packages/ion-bazan/composer-diff) [![Latest version](https://img.shields.io/packagist/v/ion-bazan/composer-diff.svg)](https://packagist.org/packages/ion-bazan/composer-diff) [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/IonBazan/composer-diff/Tests)](https://github.com/IonBazan/composer-diff/actions) [![PHP version](https://img.shields.io/packagist/php-v/ion-bazan/composer-diff.svg)](https://packagist.org/packages/ion-bazan/composer-diff) diff --git a/composer.json b/composer.json index 4aa3ddc..3947560 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,7 @@ "platform-check": false }, "extra": { - "class": "IonBazan\\ComposerDiff\\Plugin" + "class": "IonBazan\\ComposerDiff\\Composer\\Plugin" }, "autoload": { "psr-4": { diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..99b1de4 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,6 @@ +parameters: + level: 6 + paths: + - src + checkGenericClassInNonGenericObjectType: true + checkMissingIterableValueType: true diff --git a/src/Command/DiffCommand.php b/src/Command/DiffCommand.php index 4b92702..238a77b 100644 --- a/src/Command/DiffCommand.php +++ b/src/Command/DiffCommand.php @@ -3,8 +3,8 @@ namespace IonBazan\ComposerDiff\Command; use Composer\Command\BaseCommand; -use Composer\DependencyResolver\Operation\OperationInterface; -use Composer\DependencyResolver\Operation\UpdateOperation; +use IonBazan\ComposerDiff\Diff\DiffEntries; +use IonBazan\ComposerDiff\Diff\DiffEntry; use IonBazan\ComposerDiff\Formatter\Formatter; use IonBazan\ComposerDiff\Formatter\JsonFormatter; use IonBazan\ComposerDiff\Formatter\MarkdownListFormatter; @@ -133,8 +133,8 @@ protected function execute(InputInterface $input, OutputInterface $output) $formatter = $this->getFormatter($input, $output); - $prodOperations = array(); - $devOperations = array(); + $prodOperations = new DiffEntries(array()); + $devOperations = new DiffEntries(array()); if (!$input->getOption('no-prod')) { $prodOperations = $this->packageDiff->getPackageDiff($base, $target, false, $withPlatform); @@ -150,27 +150,24 @@ protected function execute(InputInterface $input, OutputInterface $output) } /** - * @param OperationInterface[] $prodOperations - * @param OperationInterface[] $devOperations - * * @return int Exit code */ - private function getExitCode(array $prodOperations, array $devOperations) + private function getExitCode(DiffEntries $prodEntries, DiffEntries $devEntries) { $exitCode = 0; - if (!empty($prodOperations)) { + if (count($prodEntries)) { $exitCode = self::CHANGES_PROD; - if ($this->hasDowngrades($prodOperations)) { + if ($this->hasDowngrades($prodEntries)) { $exitCode |= self::DOWNGRADES_PROD; } } - if (!empty($devOperations)) { + if (count($devEntries)) { $exitCode |= self::CHANGES_DEV; - if ($this->hasDowngrades($devOperations)) { + if ($this->hasDowngrades($devEntries)) { $exitCode |= self::DOWNGRADES_DEV; } } @@ -179,17 +176,18 @@ private function getExitCode(array $prodOperations, array $devOperations) } /** - * @param OperationInterface[] $operations - * * @return bool */ - private function hasDowngrades(array $operations) + private function hasDowngrades(DiffEntries $entries) { - $downgrades = array_filter($operations, function (OperationInterface $operation) { - return $operation instanceof UpdateOperation && !PackageDiff::isUpgrade($operation); - }); + /** @var DiffEntry $entry */ + foreach ($entries as $entry) { + if ($entry->isDowngrade()) { + return true; + } + } - return !empty($downgrades); + return false; } /** diff --git a/src/Plugin.php b/src/Composer/Plugin.php similarity index 95% rename from src/Plugin.php rename to src/Composer/Plugin.php index 841ea6a..0d816fc 100644 --- a/src/Plugin.php +++ b/src/Composer/Plugin.php @@ -1,6 +1,6 @@ + */ +class DiffEntries implements IteratorAggregate, Countable +{ + /** @var DiffEntry[] */ + private $entries; + + /** + * @param DiffEntry[] $entries + */ + public function __construct(array $entries) + { + $this->entries = $entries; + } + + /** + * @return ArrayIterator + */ + public function getIterator() + { + return new ArrayIterator($this->entries); + } + + public function count() + { + return \count($this->entries); + } +} diff --git a/src/Diff/DiffEntry.php b/src/Diff/DiffEntry.php new file mode 100644 index 0000000..7349f11 --- /dev/null +++ b/src/Diff/DiffEntry.php @@ -0,0 +1,111 @@ +operation = $operation; + $this->type = $this->determineType(); + } + + /** + * @return OperationInterface + */ + public function getOperation() + { + return $this->operation; + } + + /** + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * @return bool + */ + public function isInstall() + { + return self::TYPE_INSTALL === $this->type; + } + + /** + * @return bool + */ + public function isUpgrade() + { + return self::TYPE_UPGRADE === $this->type; + } + + /** + * @return bool + */ + public function isDowngrade() + { + return self::TYPE_DOWNGRADE === $this->type; + } + + /** + * @return bool + */ + public function isRemove() + { + return self::TYPE_REMOVE === $this->type; + } + + /** + * @return bool + */ + public function isChange() + { + return self::TYPE_CHANGE === $this->type; + } + + /** + * @return string + */ + private function determineType() + { + if ($this->operation instanceof InstallOperation) { + return self::TYPE_INSTALL; + } + + if ($this->operation instanceof UninstallOperation) { + return self::TYPE_REMOVE; + } + + if ($this->operation instanceof UpdateOperation) { + $upgrade = VersionComparator::isUpgrade($this->operation); + + if (null === $upgrade) { + return self::TYPE_CHANGE; + } + + return $upgrade ? self::TYPE_UPGRADE : self::TYPE_DOWNGRADE; + } + + return self::TYPE_CHANGE; + } +} diff --git a/src/Diff/VersionComparator.php b/src/Diff/VersionComparator.php new file mode 100644 index 0000000..6df684f --- /dev/null +++ b/src/Diff/VersionComparator.php @@ -0,0 +1,39 @@ +normalize($operation->getInitialPackage()->getVersion()); + $normalizedTo = $versionParser->normalize($operation->getTargetPackage()->getVersion()); + } catch (UnexpectedValueException $e) { + return null; // Consider as change if versions are not parseable + } + + /* @infection-ignore-all False-positive, handled by build matrix with Composer 1 installed */ + if ( + '9999999-dev' === $normalizedFrom + || '9999999-dev' === $normalizedTo // BC for Composer 1.x + || 0 === strpos($normalizedFrom, 'dev-') + || 0 === strpos($normalizedTo, 'dev-') + ) { + return null; + } + + $sorted = Semver::sort(array($normalizedTo, $normalizedFrom)); + + return $sorted[0] === $normalizedFrom; + } +} diff --git a/src/Formatter/AbstractFormatter.php b/src/Formatter/AbstractFormatter.php index 71c8de5..b8c177b 100644 --- a/src/Formatter/AbstractFormatter.php +++ b/src/Formatter/AbstractFormatter.php @@ -3,10 +3,10 @@ namespace IonBazan\ComposerDiff\Formatter; use Composer\DependencyResolver\Operation\InstallOperation; -use Composer\DependencyResolver\Operation\OperationInterface; use Composer\DependencyResolver\Operation\UninstallOperation; use Composer\DependencyResolver\Operation\UpdateOperation; use Composer\Package\PackageInterface; +use IonBazan\ComposerDiff\Diff\DiffEntry; use IonBazan\ComposerDiff\Url\GeneratorContainer; use Symfony\Component\Console\Output\OutputInterface; @@ -31,8 +31,10 @@ public function __construct(OutputInterface $output, GeneratorContainer $generat /** * @return string|null */ - public function getUrl(OperationInterface $operation) + public function getUrl(DiffEntry $entry) { + $operation = $entry->getOperation(); + if ($operation instanceof UpdateOperation) { return $this->getCompareUrl($operation->getInitialPackage(), $operation->getTargetPackage()); } diff --git a/src/Formatter/Formatter.php b/src/Formatter/Formatter.php index 246b43c..c743dde 100644 --- a/src/Formatter/Formatter.php +++ b/src/Formatter/Formatter.php @@ -2,30 +2,28 @@ namespace IonBazan\ComposerDiff\Formatter; -use Composer\DependencyResolver\Operation\OperationInterface; +use IonBazan\ComposerDiff\Diff\DiffEntries; +use IonBazan\ComposerDiff\Diff\DiffEntry; interface Formatter { /** - * @param OperationInterface[] $prodOperations - * @param OperationInterface[] $devOperations - * @param bool $withUrls + * @param bool $withUrls * * @return void */ - public function render(array $prodOperations, array $devOperations, $withUrls); + public function render(DiffEntries $prodEntries, DiffEntries $devEntries, $withUrls); /** - * @param OperationInterface[] $operations - * @param string $title - * @param bool $withUrls + * @param string $title + * @param bool $withUrls * * @return void */ - public function renderSingle(array $operations, $title, $withUrls); + public function renderSingle(DiffEntries $entries, $title, $withUrls); /** * @return string|null */ - public function getUrl(OperationInterface $operation); + public function getUrl(DiffEntry $entry); } diff --git a/src/Formatter/JsonFormatter.php b/src/Formatter/JsonFormatter.php index d6638a9..6697d01 100644 --- a/src/Formatter/JsonFormatter.php +++ b/src/Formatter/JsonFormatter.php @@ -3,30 +3,30 @@ namespace IonBazan\ComposerDiff\Formatter; use Composer\DependencyResolver\Operation\InstallOperation; -use Composer\DependencyResolver\Operation\OperationInterface; use Composer\DependencyResolver\Operation\UninstallOperation; use Composer\DependencyResolver\Operation\UpdateOperation; -use IonBazan\ComposerDiff\PackageDiff; +use IonBazan\ComposerDiff\Diff\DiffEntries; +use IonBazan\ComposerDiff\Diff\DiffEntry; class JsonFormatter extends AbstractFormatter { /** * {@inheritdoc} */ - public function render(array $prodOperations, array $devOperations, $withUrls) + public function render(DiffEntries $prodEntries, DiffEntries $devEntries, $withUrls) { $this->format(array( - 'packages' => $this->transformOperations($prodOperations, $withUrls), - 'packages-dev' => $this->transformOperations($devOperations, $withUrls), + 'packages' => $this->transformEntries($prodEntries, $withUrls), + 'packages-dev' => $this->transformEntries($devEntries, $withUrls), )); } /** * {@inheritdoc} */ - public function renderSingle(array $operations, $title, $withUrls) + public function renderSingle(DiffEntries $entries, $title, $withUrls) { - $this->format($this->transformOperations($operations, $withUrls)); + $this->format($this->transformEntries($entries, $withUrls)); } /** @@ -40,20 +40,19 @@ private function format(array $data) } /** - * @param OperationInterface[] $operations - * @param bool $withUrls + * @param bool $withUrls * * @return array> */ - private function transformOperations(array $operations, $withUrls) + private function transformEntries(DiffEntries $entries, $withUrls) { $rows = array(); - foreach ($operations as $operation) { - $row = $this->transformOperation($operation); + foreach ($entries as $entry) { + $row = $this->transformEntry($entry); if ($withUrls) { - $row['compare'] = $this->getUrl($operation); + $row['compare'] = $this->getUrl($entry); } $rows[$row['name']] = $row; @@ -65,12 +64,14 @@ private function transformOperations(array $operations, $withUrls) /** * @return array */ - private function transformOperation(OperationInterface $operation) + private function transformEntry(DiffEntry $entry) { + $operation = $entry->getOperation(); + if ($operation instanceof InstallOperation) { return array( 'name' => $operation->getPackage()->getName(), - 'operation' => 'install', + 'operation' => $entry->getType(), 'version_base' => null, 'version_target' => $operation->getPackage()->getFullPrettyVersion(), ); @@ -79,7 +80,7 @@ private function transformOperation(OperationInterface $operation) if ($operation instanceof UpdateOperation) { return array( 'name' => $operation->getInitialPackage()->getName(), - 'operation' => PackageDiff::isUpgrade($operation) ? 'upgrade' : 'downgrade', + 'operation' => $entry->getType(), 'version_base' => $operation->getInitialPackage()->getFullPrettyVersion(), 'version_target' => $operation->getTargetPackage()->getFullPrettyVersion(), ); @@ -88,7 +89,7 @@ private function transformOperation(OperationInterface $operation) if ($operation instanceof UninstallOperation) { return array( 'name' => $operation->getPackage()->getName(), - 'operation' => 'remove', + 'operation' => $entry->getType(), 'version_base' => $operation->getPackage()->getFullPrettyVersion(), 'version_target' => null, ); diff --git a/src/Formatter/MarkdownListFormatter.php b/src/Formatter/MarkdownListFormatter.php index e95e9a5..9c4f7f3 100644 --- a/src/Formatter/MarkdownListFormatter.php +++ b/src/Formatter/MarkdownListFormatter.php @@ -3,28 +3,28 @@ namespace IonBazan\ComposerDiff\Formatter; use Composer\DependencyResolver\Operation\InstallOperation; -use Composer\DependencyResolver\Operation\OperationInterface; use Composer\DependencyResolver\Operation\UninstallOperation; use Composer\DependencyResolver\Operation\UpdateOperation; -use IonBazan\ComposerDiff\PackageDiff; +use IonBazan\ComposerDiff\Diff\DiffEntries; +use IonBazan\ComposerDiff\Diff\DiffEntry; class MarkdownListFormatter extends MarkdownFormatter { /** * {@inheritdoc} */ - public function render(array $prodOperations, array $devOperations, $withUrls) + public function render(DiffEntries $prodEntries, DiffEntries $devEntries, $withUrls) { - $this->renderSingle($prodOperations, 'Prod Packages', $withUrls); - $this->renderSingle($devOperations, 'Dev Packages', $withUrls); + $this->renderSingle($prodEntries, 'Prod Packages', $withUrls); + $this->renderSingle($devEntries, 'Dev Packages', $withUrls); } /** * {@inheritdoc} */ - public function renderSingle(array $operations, $title, $withUrls) + public function renderSingle(DiffEntries $entries, $title, $withUrls) { - if (!\count($operations)) { + if (!\count($entries)) { return; } @@ -32,8 +32,8 @@ public function renderSingle(array $operations, $title, $withUrls) $this->output->writeln(str_repeat('=', strlen($title))); $this->output->writeln(''); - foreach ($operations as $operation) { - $this->output->writeln($this->getRow($operation, $withUrls)); + foreach ($entries as $entry) { + $this->output->writeln($this->getRow($entry, $withUrls)); } $this->output->writeln(''); @@ -44,10 +44,11 @@ public function renderSingle(array $operations, $title, $withUrls) * * @return string */ - private function getRow(OperationInterface $operation, $withUrls) + private function getRow(DiffEntry $entry, $withUrls) { - $url = $withUrls ? $this->formatUrl($this->getUrl($operation), 'Compare') : null; + $url = $withUrls ? $this->formatUrl($this->getUrl($entry), 'Compare') : null; $url = (null !== $url) ? ' '.$url : ''; + $operation = $entry->getOperation(); if ($operation instanceof InstallOperation) { return sprintf( @@ -59,11 +60,9 @@ private function getRow(OperationInterface $operation, $withUrls) } if ($operation instanceof UpdateOperation) { - $isUpgrade = PackageDiff::isUpgrade($operation); - return sprintf( ' - %s %s (%s => %s)%s', - $isUpgrade ? 'Upgrade' : 'Downgrade', + ucfirst($entry->getType()), $operation->getInitialPackage()->getName(), $operation->getInitialPackage()->getFullPrettyVersion(), $operation->getTargetPackage()->getFullPrettyVersion(), diff --git a/src/Formatter/MarkdownTableFormatter.php b/src/Formatter/MarkdownTableFormatter.php index 761f689..d0a02bb 100644 --- a/src/Formatter/MarkdownTableFormatter.php +++ b/src/Formatter/MarkdownTableFormatter.php @@ -3,39 +3,39 @@ namespace IonBazan\ComposerDiff\Formatter; use Composer\DependencyResolver\Operation\InstallOperation; -use Composer\DependencyResolver\Operation\OperationInterface; use Composer\DependencyResolver\Operation\UninstallOperation; use Composer\DependencyResolver\Operation\UpdateOperation; +use IonBazan\ComposerDiff\Diff\DiffEntries; +use IonBazan\ComposerDiff\Diff\DiffEntry; use IonBazan\ComposerDiff\Formatter\Helper\Table; -use IonBazan\ComposerDiff\PackageDiff; class MarkdownTableFormatter extends MarkdownFormatter { /** * {@inheritdoc} */ - public function render(array $prodOperations, array $devOperations, $withUrls) + public function render(DiffEntries $prodEntries, DiffEntries $devEntries, $withUrls) { - $this->renderSingle($prodOperations, 'Prod Packages', $withUrls); - $this->renderSingle($devOperations, 'Dev Packages', $withUrls); + $this->renderSingle($prodEntries, 'Prod Packages', $withUrls); + $this->renderSingle($devEntries, 'Dev Packages', $withUrls); } /** * {@inheritdoc} */ - public function renderSingle(array $operations, $title, $withUrls) + public function renderSingle(DiffEntries $entries, $title, $withUrls) { - if (!\count($operations)) { + if (!\count($entries)) { return; } $rows = array(); - foreach ($operations as $operation) { - $row = $this->getTableRow($operation); + foreach ($entries as $entry) { + $row = $this->getTableRow($entry); if ($withUrls) { - $row[] = $this->formatUrl($this->getUrl($operation), 'Compare'); + $row[] = $this->formatUrl($this->getUrl($entry), 'Compare'); } $rows[] = $row; @@ -55,8 +55,9 @@ public function renderSingle(array $operations, $title, $withUrls) /** * @return string[] */ - private function getTableRow(OperationInterface $operation) + private function getTableRow(DiffEntry $entry) { + $operation = $entry->getOperation(); if ($operation instanceof InstallOperation) { return array( $operation->getPackage()->getName(), @@ -69,7 +70,7 @@ private function getTableRow(OperationInterface $operation) if ($operation instanceof UpdateOperation) { return array( $operation->getInitialPackage()->getName(), - PackageDiff::isUpgrade($operation) ? 'Upgraded' : 'Downgraded', + $entry->isChange() ? 'Changed' : ($entry->isUpgrade() ? 'Upgraded' : 'Downgraded'), $operation->getInitialPackage()->getFullPrettyVersion(), $operation->getTargetPackage()->getFullPrettyVersion(), ); diff --git a/src/PackageDiff.php b/src/PackageDiff.php index 8b76abb..c94c035 100644 --- a/src/PackageDiff.php +++ b/src/PackageDiff.php @@ -9,9 +9,8 @@ use Composer\Package\CompletePackage; use Composer\Package\Loader\ArrayLoader; use Composer\Repository\ArrayRepository; -use Composer\Semver\Semver; -use Composer\Semver\VersionParser; -use UnexpectedValueException; +use IonBazan\ComposerDiff\Diff\DiffEntries; +use IonBazan\ComposerDiff\Diff\DiffEntry; class PackageDiff { @@ -23,7 +22,7 @@ class PackageDiff * @param bool $dev * @param bool $withPlatform * - * @return OperationInterface[] + * @return DiffEntries */ public function getPackageDiff($from, $to, $dev, $withPlatform) { @@ -50,24 +49,9 @@ public function getPackageDiff($from, $to, $dev, $withPlatform) } } - return $operations; - } - - /** - * @return bool - */ - public static function isUpgrade(UpdateOperation $operation) - { - $versionParser = new VersionParser(); - try { - $normalizedFrom = $versionParser->normalize($operation->getInitialPackage()->getVersion()); - $normalizedTo = $versionParser->normalize($operation->getTargetPackage()->getVersion()); - } catch (UnexpectedValueException $e) { - return true; // Consider as upgrade if versions are not parsable - } - $sorted = Semver::sort(array($normalizedTo, $normalizedFrom)); - - return $sorted[0] === $normalizedFrom; + return new DiffEntries(array_map(function (OperationInterface $operation) { + return new DiffEntry($operation); + }, $operations)); } /** diff --git a/tests/Command/DiffCommandTest.php b/tests/Command/DiffCommandTest.php index acf24b4..7d8c742 100644 --- a/tests/Command/DiffCommandTest.php +++ b/tests/Command/DiffCommandTest.php @@ -24,7 +24,7 @@ public function testItGeneratesReportInGivenFormat($expectedOutput, array $optio $diff->expects($this->once()) ->method('getPackageDiff') ->with($this->isType('string'), $this->isType('string'), false, false) - ->willReturn(array( + ->willReturn($this->getEntries(array( new InstallOperation($this->getPackageWithSource('a/package-1', '1.0.0', 'github.com')), new UpdateOperation($this->getPackageWithSource('a/package-2', '1.0.0', 'github.com'), $this->getPackageWithSource('a/package-2', '1.2.0', 'github.com')), new UninstallOperation($this->getPackageWithSource('a/package-3', '0.1.1', 'github.com')), @@ -32,7 +32,7 @@ public function testItGeneratesReportInGivenFormat($expectedOutput, array $optio new UninstallOperation($this->getPackageWithSource('a/package-5', '0.1.1', 'gitlab2.org')), new UninstallOperation($this->getPackageWithSource('a/package-6', '0.1.1', 'gitlab3.org')), new UpdateOperation($this->getPackageWithSource('a/package-7', '1.2.0', 'github.com'), $this->getPackageWithSource('a/package-7', '1.0.0', 'github.com')), - )) + ))) ; $result = $tester->execute($options); $this->assertSame(0, $result); @@ -53,7 +53,7 @@ public function testStrictMode($exitCode, array $prodOperations, array $devOpera $diff->expects($this->exactly(2)) ->method('getPackageDiff') ->with($this->isType('string'), $this->isType('string'), $this->isType('boolean'), false) - ->willReturnOnConsecutiveCalls($prodOperations, $devOperations) + ->willReturnOnConsecutiveCalls($this->getEntries($prodOperations), $this->getEntries($devOperations)) ; $this->assertSame($exitCode, $tester->execute(array('--strict' => null))); } diff --git a/tests/PluginTest.php b/tests/Composer/PluginTest.php similarity index 81% rename from tests/PluginTest.php rename to tests/Composer/PluginTest.php index 4e49f7f..9afb35f 100644 --- a/tests/PluginTest.php +++ b/tests/Composer/PluginTest.php @@ -1,8 +1,9 @@ assertSame($expectedType, $entry->getType()); + $this->assertTrue($entry->{'is'.ucfirst($expectedType)}()); + } + + public function operationTypeProvider() + { + return array( + 'Install operation' => array( + 'install', + new InstallOperation($this->getPackage('a/package-1', '1.0.0')), + ), + 'Remove operation' => array( + 'remove', + new UninstallOperation($this->getPackage('a/package-1', '1.0.0')), + ), + 'Upgrade operation' => array( + 'upgrade', + new UpdateOperation($this->getPackage('a/package-1', '1.0.0'), $this->getPackage('a/package-1', '2.0.0')), + ), + 'Downgrade operation' => array( + 'downgrade', + new UpdateOperation($this->getPackage('a/package-1', '2.0.0'), $this->getPackage('a/package-1', '1.0.0')), + ), + 'Change operation (base branch)' => array( + 'change', + new UpdateOperation($this->getPackage('a/package-1', 'dev-master', 'dev-master 1234567'), $this->getPackage('a/package-1', '1.0.0')), + ), + 'Change operation (target branch)' => array( + 'change', + new UpdateOperation($this->getPackage('a/package-1', '1.0.0'), $this->getPackage('a/package-1', 'dev-master', 'dev-master 1234567')), + ), + 'Change operation (both branch)' => array( + 'change', + new UpdateOperation($this->getPackage('a/package-1', 'dev-master', 'dev-master 7654321'), $this->getPackage('a/package-1', 'dev-master', 'dev-master 1234567')), + ), + 'Change operation (both custom branch)' => array( + 'change', + new UpdateOperation($this->getPackage('a/package-1', 'dev-develop', 'dev-develop 7654321'), $this->getPackage('a/package-1', 'dev-develop', 'dev-develop 1234567')), + ), + 'Change operation (target branch and base custom branch)' => array( + 'change', + new UpdateOperation($this->getPackage('a/package-1', 'dev-develop', 'dev-develop 7654321'), $this->getPackage('a/package-1', 'dev-master', 'dev-master 1234567')), + ), + 'Change operation (base branch and target custom branch)' => array( + 'change', + new UpdateOperation($this->getPackage('a/package-1', 'dev-master', 'dev-master 7654321'), $this->getPackage('a/package-1', 'dev-develop', 'dev-develop 1234567')), + ), + 'Change operation (target custom branch)' => array( + 'change', + new UpdateOperation($this->getPackage('a/package-1', '1.0.0'), $this->getPackage('a/package-1', 'dev-develop', 'dev-develop 1234567')), + ), + 'Change operation (base custom branch)' => array( + 'change', + new UpdateOperation($this->getPackage('a/package-1', 'dev-develop', 'dev-develop 7654321'), $this->getPackage('a/package-1', '1.0.0')), + ), + 'Change operation (BC with Composer 1 master as base)' => array( + 'change', + new UpdateOperation($this->getPackage('a/package-1', 'master', 'master 7654321'), $this->getPackage('a/package-1', '1.0.0')), + ), + 'Change operation (BC with Composer 1 master as target)' => array( + 'change', + new UpdateOperation($this->getPackage('a/package-1', '1.0.0'), $this->getPackage('a/package-1', 'master', 'master 1234567')), + ), + ); + } +} diff --git a/tests/Formatter/FormatterTest.php b/tests/Formatter/FormatterTest.php index 17504e8..d939c0c 100644 --- a/tests/Formatter/FormatterTest.php +++ b/tests/Formatter/FormatterTest.php @@ -6,6 +6,8 @@ use Composer\DependencyResolver\Operation\UninstallOperation; use Composer\DependencyResolver\Operation\UpdateOperation; use Composer\Package\PackageInterface; +use IonBazan\ComposerDiff\Diff\DiffEntries; +use IonBazan\ComposerDiff\Diff\DiffEntry; use IonBazan\ComposerDiff\Formatter\Formatter; use IonBazan\ComposerDiff\Tests\TestCase; use IonBazan\ComposerDiff\Url\GeneratorContainer; @@ -19,7 +21,7 @@ public function testItNoopsWhenListIsEmpty() { $output = new StreamOutput(fopen('php://memory', 'wb', false)); $formatter = $this->getFormatter($output, $this->getGenerators()); - $formatter->render(array(), array(), true); + $formatter->render(new DiffEntries(array()), new DiffEntries(array()), true); $this->assertSame(static::getEmptyOutput(), $this->getDisplay($output)); } @@ -28,7 +30,7 @@ public function testGetUrlReturnsNullForInvalidOperation() $output = $this->getMockBuilder('Symfony\Component\Console\Output\OutputInterface')->getMock(); $operation = $this->getMockBuilder('Composer\DependencyResolver\Operation\OperationInterface')->getMock(); $formatter = $this->getFormatter($output, $this->getGenerators()); - $this->assertNull($formatter->getUrl($operation)); + $this->assertNull($formatter->getUrl(new DiffEntry($operation))); } /** @@ -54,7 +56,7 @@ public function testItRendersTheListOfOperations($withUrls) new UninstallOperation($this->getPackage('a/package-4', '0.1.1')), new UninstallOperation($this->getPackage('a/no-link-2', '0.1.1')), ); - $formatter->render($prodPackages, $devPackages, $withUrls); + $formatter->render($this->getEntries($prodPackages), $this->getEntries($devPackages), $withUrls); $this->assertSame($this->getSampleOutput($withUrls), $this->getDisplay($output)); } @@ -62,9 +64,9 @@ public function testItFailsWithInvalidOperation() { $output = $this->getMockBuilder('Symfony\Component\Console\Output\OutputInterface')->getMock(); $this->setExpectedException('InvalidArgumentException', 'Invalid operation'); - $this->getFormatter($output, $this->getGenerators())->render(array( + $this->getFormatter($output, $this->getGenerators())->render($this->getEntries(array( $this->getMockBuilder('Composer\DependencyResolver\Operation\OperationInterface')->getMock(), - ), array(), false); + )), $this->getEntries(array()), false); } /** diff --git a/tests/Formatter/JsonFormatterTest.php b/tests/Formatter/JsonFormatterTest.php index ee92d96..d5df1be 100644 --- a/tests/Formatter/JsonFormatterTest.php +++ b/tests/Formatter/JsonFormatterTest.php @@ -26,7 +26,7 @@ public function testRenderSingle() ); $output = new StreamOutput(fopen('php://memory', 'wb', false)); $formatter = $this->getFormatter($output, $this->getGenerators()); - $formatter->renderSingle($sampleData, 'test', true); + $formatter->renderSingle($this->getEntries($sampleData), 'test', true); $this->assertSame(self::formatOutput(array( 'a/package-1' => array( @@ -66,7 +66,7 @@ public function testRenderSingle() ), 'a/package-5' => array( 'name' => 'a/package-5', - 'operation' => 'downgrade', + 'operation' => 'change', 'version_base' => 'dev-master 1234567', 'version_target' => '1.1.1', 'compare' => 'https://example.com/c/dev-master..1.1.1', @@ -123,7 +123,7 @@ protected function getSampleOutput($withUrls) ), 'php' => array( 'name' => 'php', - 'operation' => 'upgrade', + 'operation' => 'change', 'version_base' => '>=7.4.6', 'version_target' => '^8.0', 'compare' => null, @@ -132,7 +132,7 @@ protected function getSampleOutput($withUrls) 'packages-dev' => array( 'a/package-5' => array( 'name' => 'a/package-5', - 'operation' => 'downgrade', + 'operation' => 'change', 'version_base' => 'dev-master 1234567', 'version_target' => '1.1.1', 'compare' => 'https://example.com/c/dev-master..1.1.1', @@ -189,7 +189,7 @@ protected function getSampleOutput($withUrls) ), 'php' => array( 'name' => 'php', - 'operation' => 'upgrade', + 'operation' => 'change', 'version_base' => '>=7.4.6', 'version_target' => '^8.0', ), @@ -197,7 +197,7 @@ protected function getSampleOutput($withUrls) 'packages-dev' => array( 'a/package-5' => array( 'name' => 'a/package-5', - 'operation' => 'downgrade', + 'operation' => 'change', 'version_base' => 'dev-master 1234567', 'version_target' => '1.1.1', ), diff --git a/tests/Formatter/MarkdownListFormatterTest.php b/tests/Formatter/MarkdownListFormatterTest.php index bfcf9a1..7f97ac8 100644 --- a/tests/Formatter/MarkdownListFormatterTest.php +++ b/tests/Formatter/MarkdownListFormatterTest.php @@ -20,12 +20,12 @@ protected function getSampleOutput($withUrls) - Upgrade a/package-2 (1.0.0 => 1.2.0) [Compare](https://example.com/c/1.0.0..1.2.0) - Downgrade a/package-3 (2.0.0 => 1.1.1) [Compare](https://example.com/c/2.0.0..1.1.1) - Downgrade a/no-link-2 (2.0.0 => 1.1.1) - - Upgrade php (>=7.4.6 => ^8.0) + - Change php (>=7.4.6 => ^8.0) Dev Packages ============ - - Downgrade a/package-5 (dev-master 1234567 => 1.1.1) [Compare](https://example.com/c/dev-master..1.1.1) + - Change a/package-5 (dev-master 1234567 => 1.1.1) [Compare](https://example.com/c/dev-master..1.1.1) - Uninstall a/package-4 (0.1.1) [Compare](https://example.com/r/0.1.1) - Uninstall a/no-link-2 (0.1.1) @@ -42,12 +42,12 @@ protected function getSampleOutput($withUrls) - Upgrade a/package-2 (1.0.0 => 1.2.0) - Downgrade a/package-3 (2.0.0 => 1.1.1) - Downgrade a/no-link-2 (2.0.0 => 1.1.1) - - Upgrade php (>=7.4.6 => ^8.0) + - Change php (>=7.4.6 => ^8.0) Dev Packages ============ - - Downgrade a/package-5 (dev-master 1234567 => 1.1.1) + - Change a/package-5 (dev-master 1234567 => 1.1.1) - Uninstall a/package-4 (0.1.1) - Uninstall a/no-link-2 (0.1.1) diff --git a/tests/Formatter/MarkdownTableFormatterTest.php b/tests/Formatter/MarkdownTableFormatterTest.php index edb118b..1d1ba76 100644 --- a/tests/Formatter/MarkdownTableFormatterTest.php +++ b/tests/Formatter/MarkdownTableFormatterTest.php @@ -19,13 +19,13 @@ protected function getSampleOutput($withUrls) | a/package-2 | Upgraded | 1.0.0 | 1.2.0 | [Compare](https://example.com/c/1.0.0..1.2.0) | | a/package-3 | Downgraded | 2.0.0 | 1.1.1 | [Compare](https://example.com/c/2.0.0..1.1.1) | | a/no-link-2 | Downgraded | 2.0.0 | 1.1.1 | | -| php | Upgraded | >=7.4.6 | ^8.0 | | +| php | Changed | >=7.4.6 | ^8.0 | | -| Dev Packages | Operation | Base | Target | Link | -|--------------|------------|--------------------|--------|----------------------------------------------------| -| a/package-5 | Downgraded | dev-master 1234567 | 1.1.1 | [Compare](https://example.com/c/dev-master..1.1.1) | -| a/package-4 | Removed | 0.1.1 | - | [Compare](https://example.com/r/0.1.1) | -| a/no-link-2 | Removed | 0.1.1 | - | | +| Dev Packages | Operation | Base | Target | Link | +|--------------|-----------|--------------------|--------|----------------------------------------------------| +| a/package-5 | Changed | dev-master 1234567 | 1.1.1 | [Compare](https://example.com/c/dev-master..1.1.1) | +| a/package-4 | Removed | 0.1.1 | - | [Compare](https://example.com/r/0.1.1) | +| a/no-link-2 | Removed | 0.1.1 | - | | OUTPUT; @@ -39,13 +39,13 @@ protected function getSampleOutput($withUrls) | a/package-2 | Upgraded | 1.0.0 | 1.2.0 | | a/package-3 | Downgraded | 2.0.0 | 1.1.1 | | a/no-link-2 | Downgraded | 2.0.0 | 1.1.1 | -| php | Upgraded | >=7.4.6 | ^8.0 | +| php | Changed | >=7.4.6 | ^8.0 | -| Dev Packages | Operation | Base | Target | -|--------------|------------|--------------------|--------| -| a/package-5 | Downgraded | dev-master 1234567 | 1.1.1 | -| a/package-4 | Removed | 0.1.1 | - | -| a/no-link-2 | Removed | 0.1.1 | - | +| Dev Packages | Operation | Base | Target | +|--------------|-----------|--------------------|--------| +| a/package-5 | Changed | dev-master 1234567 | 1.1.1 | +| a/package-4 | Removed | 0.1.1 | - | +| a/no-link-2 | Removed | 0.1.1 | - | OUTPUT; diff --git a/tests/PackageDiffTest.php b/tests/PackageDiffTest.php index babdfae..10ba6db 100644 --- a/tests/PackageDiffTest.php +++ b/tests/PackageDiffTest.php @@ -3,9 +3,9 @@ namespace IonBazan\ComposerDiff\Tests; use Composer\DependencyResolver\Operation\InstallOperation; -use Composer\DependencyResolver\Operation\OperationInterface; use Composer\DependencyResolver\Operation\UninstallOperation; use Composer\DependencyResolver\Operation\UpdateOperation; +use IonBazan\ComposerDiff\Diff\DiffEntry; use IonBazan\ComposerDiff\PackageDiff; class PackageDiffTest extends TestCase @@ -27,7 +27,7 @@ public function testBasicUsage(array $expected, $dev, $withPlatform) $withPlatform ); - $this->assertSame($expected, array_map(array($this, 'operationToString'), $operations)); + $this->assertSame($expected, array_map(array($this, 'entryToString'), $operations->getIterator()->getArrayCopy())); } public function testSameBaseAndTarget() @@ -56,7 +56,7 @@ public function testGitUsage(array $expected, $dev, $withPlatform) $this->prepareGit(); $operations = $diff->getPackageDiff('HEAD', '', $dev, $withPlatform); - $this->assertSame($expected, array_map(array($this, 'operationToString'), $operations)); + $this->assertSame($expected, array_map(array($this, 'entryToString'), $operations->getIterator()->getArrayCopy())); } public function testInvalidGitRef() @@ -135,8 +135,10 @@ private function prepareGit() file_put_contents($gitDir.'/composer.lock', file_get_contents(__DIR__.'/fixtures/target/composer.lock')); } - private function operationToString(OperationInterface $operation) + private function entryToString(DiffEntry $entry) { + $operation = $entry->getOperation(); + if ($operation instanceof InstallOperation) { return sprintf('install %s %s', $operation->getPackage()->getName(), $operation->getPackage()->getPrettyVersion()); } diff --git a/tests/TestCase.php b/tests/TestCase.php index 792d4c9..e5f4bb1 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,7 +2,10 @@ namespace IonBazan\ComposerDiff\Tests; +use Composer\DependencyResolver\Operation\OperationInterface; use Composer\Package\PackageInterface; +use IonBazan\ComposerDiff\Diff\DiffEntries; +use IonBazan\ComposerDiff\Diff\DiffEntry; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase as BaseTestCase; @@ -58,4 +61,16 @@ protected function getPackageWithSource($name, $version, $sourceUrl, $sourceRefe return $package; } + + /** + * @param OperationInterface[] $operations + * + * @return DiffEntries + */ + protected function getEntries(array $operations) + { + return new DiffEntries(array_map(function (OperationInterface $operation) { + return new DiffEntry($operation); + }, $operations)); + } } From 38aa4c371c0be617119192b10f3e197e4aab4106 Mon Sep 17 00:00:00 2001 From: Ion Bazan Date: Tue, 15 Jun 2021 13:55:48 +0800 Subject: [PATCH 2/3] fix shields --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 46cfda6..f512408 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ [![PHP 5.3+ | 7.x | 8.x](https://img.shields.io/badge/PHP-^5.3_|_^7_|_^8-blue.svg)](https://packagist.org/packages/ion-bazan/composer-diff) [![Composer v1 | v2](https://img.shields.io/badge/Composer-^1.1_|_^2-success.svg)](https://packagist.org/packages/ion-bazan/composer-diff) +[![Dependencies: 0](https://img.shields.io/badge/dependencies-0-success.svg)](https://packagist.org/packages/ion-bazan/composer-diff) [![Latest version](https://img.shields.io/packagist/v/ion-bazan/composer-diff.svg)](https://packagist.org/packages/ion-bazan/composer-diff) [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/IonBazan/composer-diff/Tests)](https://github.com/IonBazan/composer-diff/actions) -[![PHP version](https://img.shields.io/packagist/php-v/ion-bazan/composer-diff.svg)](https://packagist.org/packages/ion-bazan/composer-diff) [![Codecov](https://img.shields.io/codecov/c/gh/IonBazan/composer-diff)](https://codecov.io/gh/IonBazan/composer-diff) [![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2FIonBazan%2Fcomposer-diff%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/IonBazan/composer-diff/master) [![Downloads](https://img.shields.io/packagist/dt/ion-bazan/composer-diff.svg)](https://packagist.org/packages/ion-bazan/composer-diff) From b44fcd2efcdae7053e7f5f9829ba98c9bc5662c9 Mon Sep 17 00:00:00 2001 From: Ion Bazan Date: Tue, 15 Jun 2021 16:08:18 +0800 Subject: [PATCH 3/3] add plugin integration test run --- .github/workflows/test.yml | 3 +++ phpunit.xml.dist | 1 + tests/Integration/DiffCommandTest.php | 36 +++++++++++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 66414be..4ffd0ac 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,6 +21,9 @@ jobs: - '8.0' - '8.1' include: + - php-versions: '7.0' + composer-flags: '--prefer-lowest' + operating-system: ubuntu-latest - php-versions: '5.3' composer-flags: '--prefer-lowest' operating-system: ubuntu-latest diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 1f10f73..a245aa7 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -16,6 +16,7 @@ > + diff --git a/tests/Integration/DiffCommandTest.php b/tests/Integration/DiffCommandTest.php index e06fcf6..6ca8953 100644 --- a/tests/Integration/DiffCommandTest.php +++ b/tests/Integration/DiffCommandTest.php @@ -2,9 +2,14 @@ namespace IonBazan\ComposerDiff\Tests\Integration; +use Composer\Composer; +use Composer\Console\Application; +use Composer\IO\IOInterface; +use Composer\IO\NullIO; use IonBazan\ComposerDiff\Command\DiffCommand; use IonBazan\ComposerDiff\PackageDiff; use IonBazan\ComposerDiff\Tests\TestCase; +use Symfony\Component\Console\Tester\ApplicationTester; use Symfony\Component\Console\Tester\CommandTester; class DiffCommandTest extends TestCase @@ -22,6 +27,29 @@ public function testCommand($expectedOutput, array $input) $this->assertSame($expectedOutput, $tester->getDisplay()); } + /** + * @param string $expectedOutput + * + * @dataProvider commandArgumentsDataProvider + */ + public function testComposerApplication($expectedOutput, array $input) + { + if (version_compare('2.0', Composer::VERSION, '>=')) { + $this->markTestSkipped('This test works properly only on Composer 2'); + } + + array_unshift($input, 'diff'); + $app = new ComposerApplication(); + $app->setIO(new NullIO()); + $app->setAutoExit(false); + $composer = $app->getComposer(); + $composer->getPluginManager()->registerPackage($composer->getPackage(), true); + $tester = new ApplicationTester($app); + $result = $tester->run($input); + $this->assertSame(0, $result); + $this->assertSame($expectedOutput, $tester->getDisplay()); + } + public function commandArgumentsDataProvider() { return array( @@ -194,3 +222,11 @@ public function commandArgumentsDataProvider() ); } } + +class ComposerApplication extends Application +{ + public function setIO(IOInterface $io) + { + $this->io = $io; + } +}