From 51665a595576e1d132a5e6e068a2b5846ef73dc3 Mon Sep 17 00:00:00 2001 From: Ion Bazan Date: Wed, 11 May 2022 00:49:16 +0800 Subject: [PATCH 1/3] Add hyperlinks support --- .github/workflows/test.yml | 4 +-- infection.json.dist | 5 +++ src/Diff/DiffEntry.php | 19 +++++++++++ src/Formatter/AbstractFormatter.php | 40 ++++++++++-------------- src/Formatter/MarkdownTableFormatter.php | 14 +++------ src/Url/GeneratorContainer.php | 34 +++++++++++++++++++- tests/Formatter/FormatterTest.php | 1 + tests/TestCase.php | 2 +- tests/Url/GeneratorContainerTest.php | 8 +++++ 9 files changed, 90 insertions(+), 37 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6c7d082..7fa11fa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -59,14 +59,14 @@ jobs: - name: Upload coverage to Codecov uses: codecov/codecov-action@v1 - name: Run mutation tests - if: ${{ matrix.php-versions == 8.0 && matrix.operating-system == 'ubuntu-latest' }} + if: ${{ matrix.php-versions == 8.1 && matrix.operating-system == 'ubuntu-latest' }} env: STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} run: | composer req infection/infection -W vendor/bin/infection --ignore-msi-with-no-mutations --min-covered-msi=100 --min-msi=100 -s -j4 - name: Run phpstan - if: ${{ matrix.php-versions == 8.0 && matrix.operating-system == 'ubuntu-latest' }} + if: ${{ matrix.php-versions == 8.1 && matrix.operating-system == 'ubuntu-latest' }} run: | composer req phpstan/phpstan vendor/bin/phpstan diff --git a/infection.json.dist b/infection.json.dist index 3e97690..6d233ed 100644 --- a/infection.json.dist +++ b/infection.json.dist @@ -16,6 +16,11 @@ "ignore": [ "IonBazan\\ComposerDiff\\Formatter\\JsonFormatter::format" ] + }, + "Ternary": { + "ignore": [ + "IonBazan\\ComposerDiff\\Formatter\\AbstractFormatter::terminalLink" + ] } } } diff --git a/src/Diff/DiffEntry.php b/src/Diff/DiffEntry.php index 7349f11..2e6f26d 100644 --- a/src/Diff/DiffEntry.php +++ b/src/Diff/DiffEntry.php @@ -6,6 +6,7 @@ use Composer\DependencyResolver\Operation\OperationInterface; use Composer\DependencyResolver\Operation\UninstallOperation; use Composer\DependencyResolver\Operation\UpdateOperation; +use Composer\Package\PackageInterface; class DiffEntry { @@ -83,6 +84,24 @@ public function isChange() return self::TYPE_CHANGE === $this->type; } + /** + * @return PackageInterface|null + */ + public function getPackage() + { + $operation = $this->getOperation(); + + if ($operation instanceof UpdateOperation) { + return $operation->getInitialPackage(); + } + + if ($operation instanceof InstallOperation || $operation instanceof UninstallOperation) { + return $operation->getPackage(); + } + + return null; + } + /** * @return string */ diff --git a/src/Formatter/AbstractFormatter.php b/src/Formatter/AbstractFormatter.php index d617929..1e024aa 100644 --- a/src/Formatter/AbstractFormatter.php +++ b/src/Formatter/AbstractFormatter.php @@ -6,7 +6,6 @@ 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; @@ -37,11 +36,11 @@ public function getUrl(DiffEntry $entry) $operation = $entry->getOperation(); if ($operation instanceof UpdateOperation) { - return $this->getCompareUrl($operation->getInitialPackage(), $operation->getTargetPackage()); + return $this->generators->getCompareUrl($operation->getInitialPackage(), $operation->getTargetPackage()); } if ($operation instanceof InstallOperation || $operation instanceof UninstallOperation) { - return $this->getReleaseUrl($operation->getPackage()); + return $this->generators->getReleaseUrl($operation->getPackage()); } return null; @@ -64,40 +63,35 @@ public function getProjectUrl(OperationInterface $operation) return null; } - $generator = $this->generators->get($package); - - if (!$generator) { - return null; - } - - return $generator->getProjectUrl($package); + return $this->generators->getProjectUrl($package); } /** - * @return string|null + * @return string */ - private function getCompareUrl(PackageInterface $basePackage, PackageInterface $targetPackage) + protected function getDecoratedPackageName(DiffEntry $entry) { - $generator = $this->generators->get($targetPackage); + $package = $entry->getPackage(); - if (!$generator) { - return null; + if (null === $package) { + return ''; } - return $generator->getCompareUrl($basePackage, $targetPackage); + return $this->terminalLink($this->getProjectUrl($entry->getOperation()), $package->getName()); } /** - * @return string|null + * @param string|null $url + * @param string $title + * + * @return string */ - private function getReleaseUrl(PackageInterface $package) + private function terminalLink($url, $title) { - $generator = $this->generators->get($package); - - if (!$generator) { - return null; + if (null === $url) { + return $title; } - return $generator->getReleaseUrl($package); + return method_exists('Symfony\Component\Console\Formatter\OutputFormatterStyle', 'setHref') ? sprintf('%s', $url, $title) : $title; } } diff --git a/src/Formatter/MarkdownTableFormatter.php b/src/Formatter/MarkdownTableFormatter.php index d55b3ce..33ee5e0 100644 --- a/src/Formatter/MarkdownTableFormatter.php +++ b/src/Formatter/MarkdownTableFormatter.php @@ -60,10 +60,10 @@ public function renderSingle(DiffEntries $entries, $title, $withUrls) private function getTableRow(DiffEntry $entry, $withUrls) { $operation = $entry->getOperation(); - if ($operation instanceof InstallOperation) { - $packageName = $operation->getPackage()->getName(); - $packageUrl = $withUrls ? $this->formatUrl($this->getProjectUrl($operation), $packageName) : $packageName; + $packageName = $this->getDecoratedPackageName($entry); + $packageUrl = $withUrls ? $this->formatUrl($this->getProjectUrl($operation), $packageName) : $packageName; + if ($operation instanceof InstallOperation) { return array( $packageUrl ?: $packageName, 'New', @@ -73,11 +73,8 @@ private function getTableRow(DiffEntry $entry, $withUrls) } if ($operation instanceof UpdateOperation) { - $packageName = $operation->getInitialPackage()->getName(); - $projectUrl = $withUrls ? $this->formatUrl($this->getProjectUrl($operation), $packageName) : $packageName; - return array( - $projectUrl ?: $packageName, + $packageUrl ?: $packageName, $entry->isChange() ? 'Changed' : ($entry->isUpgrade() ? 'Upgraded' : 'Downgraded'), $operation->getInitialPackage()->getFullPrettyVersion(), $operation->getTargetPackage()->getFullPrettyVersion(), @@ -85,9 +82,6 @@ private function getTableRow(DiffEntry $entry, $withUrls) } if ($operation instanceof UninstallOperation) { - $packageName = $operation->getPackage()->getName(); - $packageUrl = $withUrls ? $this->formatUrl($this->getProjectUrl($operation), $packageName) : $packageName; - return array( $packageUrl ?: $packageName, 'Removed', diff --git a/src/Url/GeneratorContainer.php b/src/Url/GeneratorContainer.php index cbd2b17..a322d7b 100644 --- a/src/Url/GeneratorContainer.php +++ b/src/Url/GeneratorContainer.php @@ -4,7 +4,7 @@ use Composer\Package\PackageInterface; -class GeneratorContainer +class GeneratorContainer implements UrlGenerator { /** * @var UrlGenerator[] @@ -42,4 +42,36 @@ public function get(PackageInterface $package) return null; } + + public function supportsPackage(PackageInterface $package) + { + return null !== $this->get($package); + } + + public function getCompareUrl(PackageInterface $initialPackage, PackageInterface $targetPackage) + { + if (!$generator = $this->get($targetPackage)) { + return null; + } + + return $generator->getCompareUrl($initialPackage, $targetPackage); + } + + public function getReleaseUrl(PackageInterface $package) + { + if (!$generator = $this->get($package)) { + return null; + } + + return $generator->getReleaseUrl($package); + } + + public function getProjectUrl(PackageInterface $package) + { + if (!$generator = $this->get($package)) { + return null; + } + + return $generator->getProjectUrl($package); + } } diff --git a/tests/Formatter/FormatterTest.php b/tests/Formatter/FormatterTest.php index 8839697..435ff99 100644 --- a/tests/Formatter/FormatterTest.php +++ b/tests/Formatter/FormatterTest.php @@ -125,6 +125,7 @@ protected function getGenerators() $generators = $this->getMockBuilder('IonBazan\ComposerDiff\Url\GeneratorContainer') ->disableOriginalConstructor() + ->setMethods(array('get')) ->getMock(); $generators->method('get') ->willReturnCallback(function (PackageInterface $package) use ($generator) { diff --git a/tests/TestCase.php b/tests/TestCase.php index 77e863b..55ac762 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -47,7 +47,7 @@ protected function getPackage($name, $version, $fullVersion = null) /** * @param string $name * @param string $version - * @param string $sourceUrl + * @param string|null $sourceUrl * @param string|null $sourceReference * * @return mixed diff --git a/tests/Url/GeneratorContainerTest.php b/tests/Url/GeneratorContainerTest.php index 9eb1603..6660204 100644 --- a/tests/Url/GeneratorContainerTest.php +++ b/tests/Url/GeneratorContainerTest.php @@ -18,5 +18,13 @@ public function testGetsProperGenerator() $this->assertInstanceOf('IonBazan\ComposerDiff\Url\GitlabGenerator', $gitlab2Generator); $this->assertNotSame($gitlabGenerator, $gitlab2Generator); $this->assertNull($container->get($this->getPackageWithSource('', '', 'https://gitlab3.org'))); + $this->assertNull($container->get($this->getPackageWithSource('', '', null))); + } + + public function testItSupportsPackageSupportedByOneOfTheGenerators() + { + $generator = new GeneratorContainer(array()); + self::assertTrue($generator->supportsPackage($this->getPackageWithSource('acme/package', '3.12.0', 'https://bitbucket.org/acme/package.git'))); + self::assertFalse($generator->supportsPackage($this->getPackageWithSource('acme/package', '3.12.0', 'https://non-existent.org/acme/package.git'))); } } From 1b05bac602ace28da818383adebedf371e7cd832 Mon Sep 17 00:00:00 2001 From: Ion Bazan Date: Wed, 11 May 2022 10:55:40 +0800 Subject: [PATCH 2/3] improve tests --- .github/workflows/test.yml | 2 +- infection.json.dist | 2 +- tests/PackageDiffTest.php | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7fa11fa..be567c4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -55,7 +55,7 @@ jobs: - name: Install Composer dependencies run: composer update -n --prefer-dist ${{ matrix.composer-flags }} - name: Run Tests - run: vendor/bin/simple-phpunit --coverage-clover coverage.xml + run: vendor/bin/simple-phpunit --coverage-clover coverage.xml --coverage-text - name: Upload coverage to Codecov uses: codecov/codecov-action@v1 - name: Run mutation tests diff --git a/infection.json.dist b/infection.json.dist index 6d233ed..30616e6 100644 --- a/infection.json.dist +++ b/infection.json.dist @@ -7,7 +7,7 @@ "logs": { "text": "infection.log", "stryker": { - "badge": "master" + "badge": "main" } }, "mutators": { diff --git a/tests/PackageDiffTest.php b/tests/PackageDiffTest.php index fde2d17..1332262 100644 --- a/tests/PackageDiffTest.php +++ b/tests/PackageDiffTest.php @@ -191,6 +191,7 @@ private function prepareGit() $gitDir = __DIR__.'/test-git'; @mkdir($gitDir); chdir($gitDir); + exec('git config init.defaultBranch main'); exec('git init'); exec('git config user.name test'); exec('git config user.email test@example.com'); From 41537c3d01c0979f2ad55acbea917d5de035f6a9 Mon Sep 17 00:00:00 2001 From: Ion Bazan Date: Wed, 11 May 2022 14:45:26 +0800 Subject: [PATCH 3/3] Add decorated output test --- .github/workflows/test.yml | 2 + tests/Formatter/FormatterTest.php | 66 ++++++++++---- tests/Formatter/GitHubFormatterTest.php | 2 +- tests/Formatter/JsonFormatterTest.php | 2 +- tests/Formatter/MarkdownListFormatterTest.php | 48 ++++++++++- .../Formatter/MarkdownTableFormatterTest.php | 86 ++++++++++++++++++- 6 files changed, 184 insertions(+), 22 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index be567c4..edd1876 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -54,6 +54,8 @@ jobs: restore-keys: ${{ runner.os }}-composer- - name: Install Composer dependencies run: composer update -n --prefer-dist ${{ matrix.composer-flags }} + - name: Set default branch for tests + run: git config --global init.defaultBranch main - name: Run Tests run: vendor/bin/simple-phpunit --coverage-clover coverage.xml --coverage-text - name: Upload coverage to Codecov diff --git a/tests/Formatter/FormatterTest.php b/tests/Formatter/FormatterTest.php index 435ff99..2ec225f 100644 --- a/tests/Formatter/FormatterTest.php +++ b/tests/Formatter/FormatterTest.php @@ -3,6 +3,7 @@ namespace IonBazan\ComposerDiff\Tests\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; @@ -43,29 +44,22 @@ public function testGetProjectUrlReturnsNullForInvalidOperation() /** * @param bool $withUrls + * @param bool $decorated * * @testWith [false] * [true] + * [false, true] + * [true, true] */ - public function testItRendersTheListOfOperations($withUrls) + public function testItRendersTheListOfOperations($withUrls, $decorated = false) { - $output = new StreamOutput(fopen('php://memory', 'wb', false)); - $formatter = $this->getFormatter($output, $this->getGenerators()); - $prodPackages = array( - new InstallOperation($this->getPackage('a/package-1', '1.0.0')), - new InstallOperation($this->getPackage('a/no-link-1', '1.0.0')), - new UpdateOperation($this->getPackage('a/package-2', '1.0.0'), $this->getPackage('a/package-2', '1.2.0')), - new UpdateOperation($this->getPackage('a/package-3', '2.0.0'), $this->getPackage('a/package-3', '1.1.1')), - new UpdateOperation($this->getPackage('a/no-link-2', '2.0.0'), $this->getPackage('a/no-link-2', '1.1.1')), - new UpdateOperation($this->getPackage('php', '>=7.4.6'), $this->getPackage('php', '^8.0')), + $output = new StreamOutput(fopen('php://memory', 'wb', false), OutputInterface::VERBOSITY_NORMAL, $decorated); + $this->getFormatter($output, $this->getGenerators())->render( + $this->getEntries($this->getSampleProdOperations()), + $this->getEntries($this->getSampleDevOperations()), + $withUrls ); - $devPackages = array( - new UpdateOperation($this->getPackage('a/package-5', 'dev-master', 'dev-master 1234567'), $this->getPackage('a/package-5', '1.1.1')), - new UninstallOperation($this->getPackage('a/package-4', '0.1.1')), - new UninstallOperation($this->getPackage('a/no-link-2', '0.1.1')), - ); - $formatter->render($this->getEntries($prodPackages), $this->getEntries($devPackages), $withUrls); - $this->assertSame($this->getSampleOutput($withUrls), $this->getDisplay($output)); + $this->assertSame($this->getSampleOutput($withUrls, $decorated), $this->getDisplay($output)); } public function testItFailsWithInvalidOperation() @@ -84,10 +78,11 @@ abstract protected function getFormatter(OutputInterface $output, GeneratorConta /** * @param bool $withUrls + * @param bool $decorated * * @return string */ - abstract protected function getSampleOutput($withUrls); + abstract protected function getSampleOutput($withUrls, $decorated); /** * @return string @@ -107,6 +102,14 @@ protected function getDisplay(OutputInterface $output) return stream_get_contents($output->getStream()); } + /** + * @return bool + */ + protected function supportsLinks() + { + return method_exists('Symfony\Component\Console\Formatter\OutputFormatterStyle', 'setHref'); + } + /** * @return MockObject|GeneratorContainer */ @@ -138,4 +141,31 @@ protected function getGenerators() return $generators; } + + /** + * @return OperationInterface[] + */ + private function getSampleProdOperations() + { + return array( + new InstallOperation($this->getPackage('a/package-1', '1.0.0')), + new InstallOperation($this->getPackage('a/no-link-1', '1.0.0')), + new UpdateOperation($this->getPackage('a/package-2', '1.0.0'), $this->getPackage('a/package-2', '1.2.0')), + new UpdateOperation($this->getPackage('a/package-3', '2.0.0'), $this->getPackage('a/package-3', '1.1.1')), + new UpdateOperation($this->getPackage('a/no-link-2', '2.0.0'), $this->getPackage('a/no-link-2', '1.1.1')), + new UpdateOperation($this->getPackage('php', '>=7.4.6'), $this->getPackage('php', '^8.0')), + ); + } + + /** + * @return OperationInterface[] + */ + private function getSampleDevOperations() + { + return array( + new UpdateOperation($this->getPackage('a/package-5', 'dev-master', 'dev-master 1234567'), $this->getPackage('a/package-5', '1.1.1')), + new UninstallOperation($this->getPackage('a/package-4', '0.1.1')), + new UninstallOperation($this->getPackage('a/no-link-2', '0.1.1')), + ); + } } diff --git a/tests/Formatter/GitHubFormatterTest.php b/tests/Formatter/GitHubFormatterTest.php index ba925b2..1f7656d 100644 --- a/tests/Formatter/GitHubFormatterTest.php +++ b/tests/Formatter/GitHubFormatterTest.php @@ -8,7 +8,7 @@ class GitHubFormatterTest extends FormatterTest { - protected function getSampleOutput($withUrls) + protected function getSampleOutput($withUrls, $decorated) { if ($withUrls) { return <<getDisplay($output)); } - protected function getSampleOutput($withUrls) + protected function getSampleOutput($withUrls, $decorated) { if ($withUrls) { return self::formatOutput(array( diff --git a/tests/Formatter/MarkdownListFormatterTest.php b/tests/Formatter/MarkdownListFormatterTest.php index 144facf..64098ef 100644 --- a/tests/Formatter/MarkdownListFormatterTest.php +++ b/tests/Formatter/MarkdownListFormatterTest.php @@ -8,9 +8,32 @@ class MarkdownListFormatterTest extends FormatterTest { - protected function getSampleOutput($withUrls) + protected function getSampleOutput($withUrls, $decorated) { if ($withUrls) { + if ($decorated) { + return << 1.2.0) [Compare](https://example.com/c/1.0.0..1.2.0) + - Downgrade [a/package-3](https://example.com/r/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) + - Change php (>=7.4.6 => ^8.0) + +Dev Packages +============ + + - Change [a/package-5](https://example.com/r/a/package-5) (dev-master 1234567 => 1.1.1) [Compare](https://example.com/c/dev-master..1.1.1) + - Uninstall [a/package-4](https://example.com/r/a/package-4) (0.1.1) [Compare](https://example.com/r/0.1.1) + - Uninstall a/no-link-2 (0.1.1) + + +OUTPUT; + } + return << 1.2.0) + - Downgrade a/package-3 (2.0.0 => 1.1.1) + - Downgrade a/no-link-2 (2.0.0 => 1.1.1) + - Change php (>=7.4.6 => ^8.0) + +Dev Packages +============ + + - 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) + + OUTPUT; } diff --git a/tests/Formatter/MarkdownTableFormatterTest.php b/tests/Formatter/MarkdownTableFormatterTest.php index f8a5873..26036a2 100644 --- a/tests/Formatter/MarkdownTableFormatterTest.php +++ b/tests/Formatter/MarkdownTableFormatterTest.php @@ -8,9 +8,51 @@ class MarkdownTableFormatterTest extends FormatterTest { - protected function getSampleOutput($withUrls) + protected function getSampleOutput($withUrls, $decorated) { if ($withUrls) { + if ($decorated) { + if ($this->supportsLinks()) { + return <<=7.4.6 | ^8.0 | | + +| Dev Packages | Operation | Base | Target | Link | +|--------------------------------------------------|-----------|--------------------|--------|----------------------------------------------------| +| []8;;https://example.com/r/a/package-5\\a/package-5]8;;\\](https://example.com/r/a/package-5) | Changed | dev-master 1234567 | 1.1.1 | [Compare](https://example.com/c/dev-master..1.1.1) | +| []8;;https://example.com/r/a/package-4\\a/package-4]8;;\\](https://example.com/r/a/package-4) | Removed | 0.1.1 | - | [Compare](https://example.com/r/0.1.1) | +| a/no-link-2 | Removed | 0.1.1 | - | | + + +OUTPUT; + } + + return <<=7.4.6 | ^8.0 | | + +| Dev Packages | Operation | Base | Target | Link | +|--------------------------------------------------|-----------|--------------------|--------|----------------------------------------------------| +| [a/package-5](https://example.com/r/a/package-5) | Changed | dev-master 1234567 | 1.1.1 | [Compare](https://example.com/c/dev-master..1.1.1) | +| [a/package-4](https://example.com/r/a/package-4) | Removed | 0.1.1 | - | [Compare](https://example.com/r/0.1.1) | +| a/no-link-2 | Removed | 0.1.1 | - | | + + +OUTPUT; + } + return <<supportsLinks()) { + return <<=7.4.6 | ^8.0 | + +| Dev Packages | Operation | Base | Target | +|--------------|-----------|--------------------|--------| +| ]8;;https://example.com/r/a/package-5\a/package-5]8;;\ | Changed | dev-master 1234567 | 1.1.1 | +| ]8;;https://example.com/r/a/package-4\a/package-4]8;;\ | Removed | 0.1.1 | - | +| a/no-link-2 | Removed | 0.1.1 | - | + + +OUTPUT; + } + + return <<=7.4.6 | ^8.0 | + +| 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; }