From 9a0db7904178a8d457a9429a70cf21bfa6bdb44d Mon Sep 17 00:00:00 2001 From: Sebastian Knott Date: Thu, 8 Feb 2024 11:57:39 +0100 Subject: [PATCH] Use different PHP version information for PHPCS and PHPStant * Split version decorators in MinimalVersionDecorator and VersionDecorator * Updated min versions of some dependencies --- composer.json | 12 +-- .../EventDispatcherFactory.php | 2 + .../PhpVersion/ComposerInterpreter.php | 49 ++++++++++ .../ConstraintToVersionConverter.php | 52 +++++++++++ .../PhpVersion/MinimalVersionDecorator.php | 43 +++++++++ ...MinimalVersionDependantTerminalCommand.php | 15 +++ .../MinimalVersionDependentTrait.php | 18 ++++ .../PhpVersion/VersionDecorator.php | 63 ++----------- .../PHPCodeSniffer/TerminalCommand.php | 11 ++- .../EventDispatcherFactoryTest.php | 2 + .../PhpVersion/ComposerInterpreterTest.php | 90 ++++++++++++++++++ .../ConstraintToVersionConverterTest.php | 58 ++++++++++++ .../MinimalVersionDecoratorTest.php | 91 +++++++++++++++++++ .../PhpVersion/VersionDecoratorTest.php | 55 ++++++----- .../vendor/dein/paket/composer.json | 4 +- .../vendor/mein/paket/composer.json | 4 +- .../fixture/places/config/composer.json | 5 +- .../fixture/places/corrupt/composer.json | 3 + .../fixture/places/none/composer.json | 3 + .../fixture/places/require/composer.json | 4 +- .../PHPCodeSniffer/TerminalCommandTest.php | 2 +- 21 files changed, 484 insertions(+), 102 deletions(-) create mode 100644 src/main/php/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/ComposerInterpreter.php create mode 100644 src/main/php/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/ConstraintToVersionConverter.php create mode 100644 src/main/php/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/MinimalVersionDecorator.php create mode 100644 src/main/php/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/MinimalVersionDependantTerminalCommand.php create mode 100644 src/main/php/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/MinimalVersionDependentTrait.php create mode 100644 tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/ComposerInterpreterTest.php create mode 100644 tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/ConstraintToVersionConverterTest.php create mode 100644 tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/MinimalVersionDecoratorTest.php create mode 100644 tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/fixture/places/corrupt/composer.json create mode 100644 tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/fixture/places/none/composer.json diff --git a/composer.json b/composer.json index 7db37f7..caab4d2 100644 --- a/composer.json +++ b/composer.json @@ -29,10 +29,10 @@ "ext-xml": "*", "composer-runtime-api": "^2.0", "bamarni/composer-bin-plugin": "^1.8", - "composer/semver": "^3.3", + "composer/semver": "^3.4", "dealerdirect/phpcodesniffer-composer-installer": "^1.0.0", "nette/neon": "^3.3", - "nikic/php-parser": "^4.15", + "nikic/php-parser": "^4.18", "php-di/php-di": "^7.0.1", "slevomat/coding-standard": "^8.10.0", "squizlabs/php_codesniffer": "^3.7.2", @@ -47,10 +47,10 @@ "amphp/amp": "^v2.6.2", "amphp/phpunit-util": "^2.0.0", "amphp/process": "^v1.1.4", - "brianium/paratest": "^6.8.1", - "infection/infection": ">=0.26.16", - "mockery/mockery": "^1.5.1", - "phpunit/phpunit": "^9.5.28", + "brianium/paratest": "^6.11.0", + "infection/infection": ">=0.27.9", + "mockery/mockery": "^1.6.7", + "phpunit/phpunit": "^9.6.16", "roave/security-advisories": "dev-latest", "sebastianknott/hamcrest-object-accessor": "^3.0.0" }, diff --git a/src/main/php/CommandLine/ApplicationLifeCycle/EventDispatcherFactory.php b/src/main/php/CommandLine/ApplicationLifeCycle/EventDispatcherFactory.php index c946b87..443f4b7 100644 --- a/src/main/php/CommandLine/ApplicationLifeCycle/EventDispatcherFactory.php +++ b/src/main/php/CommandLine/ApplicationLifeCycle/EventDispatcherFactory.php @@ -10,6 +10,7 @@ use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\Extension\FileExtensionDecorator; use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\Fix\FixDecorator; use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\Multiprocess\MultiprocessDecorator; +use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\PhpVersion\MinimalVersionDecorator; use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\PhpVersion\VersionDecorator; use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\Target\TargetDecorator; use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\Verbose\VerboseDecorator; @@ -32,6 +33,7 @@ class EventDispatcherFactory TargetDecorator::class, VerboseDecorator::class, MultiprocessDecorator::class, + MinimalVersionDecorator::class, VersionDecorator::class, ]; diff --git a/src/main/php/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/ComposerInterpreter.php b/src/main/php/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/ComposerInterpreter.php new file mode 100644 index 0000000..3be94d7 --- /dev/null +++ b/src/main/php/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/ComposerInterpreter.php @@ -0,0 +1,49 @@ +environment->getRootDirectory(); + $path = $rootDirectory->getRealPath(); + $composerFile = $this->enhancedFileInfoFactory->buildFromPath($path . '/composer.json'); + $composerConfig = json_decode( + file_get_contents($composerFile->getRealPath()), + associative: true, + flags: JSON_THROW_ON_ERROR + ); + + $phpVersionConstraint = $composerConfig['config']['platform']['php'] + ?? $composerConfig['require']['php'] + ?? '*'; + + $phpVersionConstraintExtracted = $this->constraintToVersionConverter + ->extractActualPhpVersion($phpVersionConstraint); + + return $phpVersionConstraintExtracted; + } +} diff --git a/src/main/php/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/ConstraintToVersionConverter.php b/src/main/php/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/ConstraintToVersionConverter.php new file mode 100644 index 0000000..5f6c353 --- /dev/null +++ b/src/main/php/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/ConstraintToVersionConverter.php @@ -0,0 +1,52 @@ + */ + private array $phpVersions = []; + + public function __construct() + { + $phpVersionRanges = [ + '7.4.' => '33', + '8.0.' => '27', + '8.1.' => '17', + '8.2.' => (explode('.', phpversion()))[2], + ]; + + foreach ($phpVersionRanges as $phpVersionString => $phpMaxPatchVersion) { + $phpPatchLevels = range('0', $phpMaxPatchVersion); + foreach ($phpPatchLevels as $phpPatchLevel) { + $this->phpVersions[] = $phpVersionString . $phpPatchLevel; + } + } + } + + /** + * Check if $phpVersionConstraint is a version number and return it or if we find a php version that satisfies + * the constraint. + */ + public function extractActualPhpVersion(string $phpVersionConstraint): string + { + if (preg_match('/^(\d+)(\.\d)?(\.\d)?$/', $phpVersionConstraint, $matches)) { + return $matches[1] . ($matches[2] ?? '.0') . ($matches[3] ?? '.0'); + } + + $minPhpVersion = '7.4.0'; + foreach ($this->phpVersions as $phpVersion) { + if (SemVer::satisfies($phpVersion, $phpVersionConstraint)) { + $minPhpVersion = $phpVersion; + break; + } + } + return $minPhpVersion; + } +} diff --git a/src/main/php/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/MinimalVersionDecorator.php b/src/main/php/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/MinimalVersionDecorator.php new file mode 100644 index 0000000..d534694 --- /dev/null +++ b/src/main/php/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/MinimalVersionDecorator.php @@ -0,0 +1,43 @@ +getTerminalCommand(); + + if (!$terminalCommand instanceof MinimalVersionDependantTerminalCommand) { + return; + } + + if ($this->cachedMinPhpVersion === null) { + $phpVersionConstraint = $this->composerInterpreter->getLocalPhpVersionConstraint(); + + $this->cachedMinPhpVersion = $this->constraintToVersionConverter + ->extractActualPhpVersion($phpVersionConstraint); + } + + $terminalCommand->setMinimalPhpVersion($this->cachedMinPhpVersion); + + $event->getOutput()->writeln( + 'Targeted minimal PHP version is ' . $this->cachedMinPhpVersion . '' . PHP_EOL, + OutputInterface::VERBOSITY_VERBOSE, + ); + } +} diff --git a/src/main/php/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/MinimalVersionDependantTerminalCommand.php b/src/main/php/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/MinimalVersionDependantTerminalCommand.php new file mode 100644 index 0000000..12d1f35 --- /dev/null +++ b/src/main/php/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/MinimalVersionDependantTerminalCommand.php @@ -0,0 +1,15 @@ +minimalPhpVersion = $minimalPhpVersion; + } +} diff --git a/src/main/php/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/VersionDecorator.php b/src/main/php/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/VersionDecorator.php index 38e840b..2bc16a6 100644 --- a/src/main/php/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/VersionDecorator.php +++ b/src/main/php/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/VersionDecorator.php @@ -4,42 +4,25 @@ namespace Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\PhpVersion; -use Composer\Semver\Semver; use Symfony\Component\Console\Output\OutputInterface; use Zooroyal\CodingStandard\CommandLine\EnhancedFileInfo\EnhancedFileInfo; -use Zooroyal\CodingStandard\CommandLine\EnhancedFileInfo\EnhancedFileInfoFactory; use Zooroyal\CodingStandard\CommandLine\Environment\Environment; use Zooroyal\CodingStandard\CommandLine\FileSearch\FileSearchInterface; use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\DecorateEvent; use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\TerminalCommandDecorator; use function Safe\file_get_contents; -use function Safe\preg_match; class VersionDecorator extends TerminalCommandDecorator { - /** @var array */ - private array $phpVersions = []; private ?string $cachedMinPhpVersion = null; public function __construct( private readonly Environment $environment, private readonly FileSearchInterface $fileSearchInterface, - private readonly EnhancedFileInfoFactory $enhancedFileInfoFactory, + private readonly ConstraintToVersionConverter $constraintToVersionConverter, + private readonly ComposerInterpreter $composerInterpreter, ) { - $phpVersionRanges = [ - '7.4.' => '33', - '8.0.' => '27', - '8.1.' => '17', - '8.2.' => (explode('.', phpversion()))[2], - ]; - - foreach ($phpVersionRanges as $phpVersionString => $phpMaxPatchVersion) { - $phpPatchLevels = range('0', $phpMaxPatchVersion); - foreach ($phpPatchLevels as $phpPatchLevel) { - $this->phpVersions[] = $phpVersionString . $phpPatchLevel; - } - } } public function decorate(DecorateEvent $event): void @@ -63,26 +46,6 @@ public function decorate(DecorateEvent $event): void ); } - /** - * Check if $phpVersionConstraint is a version number and return it or if we find a php version that satisfies - * the constraint. - */ - private function extractActualPhpVersion(string $phpVersionConstraint): string - { - if (preg_match('/^(\d+)(\.\d)?(\.\d)?$/', $phpVersionConstraint, $matches)) { - return $matches[1] . ($matches[2] ?? '.0') . ($matches[3] ?? '.0'); - } - - $minPhpVersion = '7.4.0'; - foreach ($this->phpVersions as $phpVersion) { - if (SemVer::satisfies($phpVersion, $phpVersionConstraint)) { - $minPhpVersion = $phpVersion; - break; - } - } - return $minPhpVersion; - } - /** * Finds all composer files in the project. * @@ -91,18 +54,15 @@ private function extractActualPhpVersion(string $phpVersionConstraint): string private function gatherComposerFiles(): array { $rootDirectory = $this->environment->getRootDirectory(); - $path = $rootDirectory->getRealPath(); - $composerFiles[] = $this->enhancedFileInfoFactory->buildFromPath($path . '/composer.json'); $foundComposerFiles = $this->fileSearchInterface->listFolderFiles( fileName: 'composer.json', path: $rootDirectory, minDepth: 1, - maxDepth: 4 + maxDepth: 4, ); - $composerFiles = [...$composerFiles, ...$foundComposerFiles]; - return $composerFiles; + return $foundComposerFiles; } /** @@ -112,22 +72,15 @@ private function gatherComposerFiles(): array */ private function searchMinimalViablePhpVersion(array $composerFiles): string { - $minPhpVersion = '7.4.0'; + $minPhpVersion = $this->composerInterpreter->getLocalPhpVersionConstraint(); - foreach ($composerFiles as $key => $composerFile) { + foreach ($composerFiles as $composerFile) { $contents = file_get_contents($composerFile->getRealPath()); $composerConfig = json_decode($contents, true, 512, JSON_THROW_ON_ERROR); - // The first file is the root composer file, so we need to check the platform config. - if ($key === 0) { - $phpVersionConstraint = $composerConfig['config']['platform']['php'] - ?? $composerConfig['require']['php'] - ?? '*'; - } else { - $phpVersionConstraint = $composerConfig['require']['php'] ?? '*'; - } + $phpVersionConstraint = $composerConfig['require']['php'] ?? '*'; - $minPhpVersionPackage = $this->extractActualPhpVersion($phpVersionConstraint); + $minPhpVersionPackage = $this->constraintToVersionConverter->extractActualPhpVersion($phpVersionConstraint); $minPhpVersion = version_compare($minPhpVersion, $minPhpVersionPackage, '<') ? $minPhpVersionPackage : $minPhpVersion; diff --git a/src/main/php/CommandLine/StaticCodeAnalysis/PHPCodeSniffer/TerminalCommand.php b/src/main/php/CommandLine/StaticCodeAnalysis/PHPCodeSniffer/TerminalCommand.php index ef6f696..41e2768 100644 --- a/src/main/php/CommandLine/StaticCodeAnalysis/PHPCodeSniffer/TerminalCommand.php +++ b/src/main/php/CommandLine/StaticCodeAnalysis/PHPCodeSniffer/TerminalCommand.php @@ -16,9 +16,10 @@ use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\Fix\FixTrait; use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\Multiprocess\MultiprocessTerminalCommand; use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\Multiprocess\MultiprocessTrait; +// phpcs:ignore -- I did not find a way to either break this line or to make it shorter. +use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\PhpVersion\MinimalVersionDependantTerminalCommand; +use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\PhpVersion\MinimalVersionDependentTrait; use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\PhpVersion\PhpVersionConverter; -use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\PhpVersion\VersionDependentTerminalCommand; -use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\PhpVersion\VersionDependentTrait; use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\Target\TargetTerminalCommand; use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\Target\TargetTrait; use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\Verbose\VerboseTerminalCommand; @@ -31,7 +32,7 @@ class TerminalCommand extends AbstractTerminalCommand implements FileExtensionTerminalCommand, VerboseTerminalCommand, MultiprocessTerminalCommand, - VersionDependentTerminalCommand + MinimalVersionDependantTerminalCommand { use TargetTrait; use FixTrait; @@ -39,7 +40,7 @@ class TerminalCommand extends AbstractTerminalCommand implements use FileExtensionTrait; use VerboseTrait; use MultiprocessTrait; - use VersionDependentTrait; + use MinimalVersionDependentTrait; private const TEMPLATE = 'php %1$s %5$s%6$s--parallel=%7$d -p --standard=%2$s%3$s%8$s%4$s'; @@ -148,7 +149,7 @@ private function buildTargetingString(): string private function buildPhpVersionString(): string { $template = ' --runtime-set php_version %d'; - $phpVersion = $this->phpVersionConverter->convertSemVerToPhpString($this->phpVersion); + $phpVersion = $this->phpVersionConverter->convertSemVerToPhpString($this->minimalPhpVersion); $result = sprintf($template, $phpVersion); diff --git a/tests/Unit/CommandLine/ApplicationLifeCycle/EventDispatcherFactoryTest.php b/tests/Unit/CommandLine/ApplicationLifeCycle/EventDispatcherFactoryTest.php index 4980b20..7d6c148 100644 --- a/tests/Unit/CommandLine/ApplicationLifeCycle/EventDispatcherFactoryTest.php +++ b/tests/Unit/CommandLine/ApplicationLifeCycle/EventDispatcherFactoryTest.php @@ -16,6 +16,7 @@ use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\Extension\FileExtensionDecorator; use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\Fix\FixDecorator; use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\Multiprocess\MultiprocessDecorator; +use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\PhpVersion\MinimalVersionDecorator; use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\PhpVersion\VersionDecorator; use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\Target\TargetDecorator; use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\Verbose\VerboseDecorator; @@ -37,6 +38,7 @@ class EventDispatcherFactoryTest extends TestCase TargetDecorator::class, VerboseDecorator::class, MultiprocessDecorator::class, + MinimalVersionDecorator::class, VersionDecorator::class, ]; diff --git a/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/ComposerInterpreterTest.php b/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/ComposerInterpreterTest.php new file mode 100644 index 0000000..8efe4e2 --- /dev/null +++ b/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/ComposerInterpreterTest.php @@ -0,0 +1,90 @@ +mockedEnvironment = Mockery::mock(Environment::class); + $this->mockedEnhancedFileInfoFactory = Mockery::mock(EnhancedFileInfoFactory::class); + $this->mockedConstraintToVersionConverter = Mockery::mock(ConstraintToVersionConverter::class); + + $this->subject = new ComposerInterpreter( + $this->mockedEnvironment, + $this->mockedEnhancedFileInfoFactory, + $this->mockedConstraintToVersionConverter + ); + } + + protected function tearDown(): void + { + Mockery::close(); + } + + /** + * @test + * @dataProvider getLocalPhpVersionConstraintReturnsPhpVersionConstraintDataProvider + */ + public function getLocalPhpVersionConstraintReturnsPhpVersionConstraint(string $path, string $expectedVersion): void + { + $expectedExtractedVersion = $expectedVersion . '.1'; + + $mockedEnhancedFileInfo = Mockery::mock(EnhancedFileInfo::class); + + $this->mockedEnvironment->allows('getRootDirectory->getRealPath')->andReturn(__DIR__ . $path); + $this->mockedEnhancedFileInfoFactory->allows()->buildFromPath(__DIR__ . $path . '/composer.json') + ->andReturn($mockedEnhancedFileInfo); + $mockedEnhancedFileInfo->allows()->getRealPath()->andReturn(__DIR__ . $path . '/composer.json'); + + $this->mockedConstraintToVersionConverter->allows()->extractActualPhpVersion($expectedVersion) + ->andReturn($expectedExtractedVersion); + + $result = $this->subject->getLocalPhpVersionConstraint(); + self::assertSame($expectedExtractedVersion, $result); + } + + /** + * @test + */ + public function getLocalPhpVersionConstraintThrowsErrorOnCorruptJson(): void + { + $this->expectException(JsonException::class); + $mockedEnhancedFileInfo = Mockery::mock(EnhancedFileInfo::class); + + $path = '/fixture/places/corrupt'; + $this->mockedEnvironment->allows('getRootDirectory->getRealPath')->andReturn(__DIR__ . $path); + $this->mockedEnhancedFileInfoFactory->allows()->buildFromPath(__DIR__ . $path . '/composer.json') + ->andReturn($mockedEnhancedFileInfo); + $mockedEnhancedFileInfo->allows()->getRealPath()->andReturn(__DIR__ . $path . '/composer.json'); + $this->subject->getLocalPhpVersionConstraint(); + } + + /** @return array> */ + public function getLocalPhpVersionConstraintReturnsPhpVersionConstraintDataProvider(): array + { + return [ + 'require' => ['path' => '/fixture/places/require', 'expectedVersion' => '8.2'], + 'config' => ['path' => '/fixture/places/config', 'expectedVersion' => '8.1'], + 'none' => ['path' => '/fixture/places/none', 'expectedVersion' => '*'], + ]; + } +} diff --git a/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/ConstraintToVersionConverterTest.php b/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/ConstraintToVersionConverterTest.php new file mode 100644 index 0000000..5daa45c --- /dev/null +++ b/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/ConstraintToVersionConverterTest.php @@ -0,0 +1,58 @@ +subject = new ConstraintToVersionConverter(); + } + + protected function tearDown(): void + { + Mockery::close(); + } + + /** + * @test + */ + public function extractActualPhpVersionWithSemVerReturnsSemVer(): void + { + $result = $this->subject->extractActualPhpVersion('1.2.3'); + + self::assertSame('1.2.3', $result); + } + + + /** + * @test + * @dataProvider extractVersionsDataProvider + */ + public function extractVersions(string $givenVersion, string $expectedVersion): void + { + $result = $this->subject->extractActualPhpVersion($givenVersion); + + self::assertSame($expectedVersion, $result); + } + + /** @return array> */ + public function extractVersionsDataProvider(): array + { + return [ + 'version 7.4' => ['givenVersion' => '>=7.4', 'expectedVersion' => '7.4.0',], + 'version 8.0' => ['givenVersion' => '8.0.1', 'expectedVersion' => '8.0.1',], + 'version 8.1' => ['givenVersion' => '^8.1', 'expectedVersion' => '8.1.0',], + 'version 8.2' => ['givenVersion' => '8.2', 'expectedVersion' => '8.2.0',], + 'version 8.placeholder' => ['givenVersion' => '8.*', 'expectedVersion' => '8.0.0',], + ]; + } +} diff --git a/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/MinimalVersionDecoratorTest.php b/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/MinimalVersionDecoratorTest.php new file mode 100644 index 0000000..1798602 --- /dev/null +++ b/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/MinimalVersionDecoratorTest.php @@ -0,0 +1,91 @@ +mockedConstraintToVersionConverter = Mockery::mock(ConstraintToVersionConverter::class); + $this->mockedComposerInterpreter = Mockery::mock(ComposerInterpreter::class); + $this->mockedEvent = Mockery::mock(DecorateEvent::class); + $this->mockedTerminalCommand = Mockery::mock(MinimalVersionDependantTerminalCommand::class); + $this->mockedOutput = Mockery::mock(OutputInterface::class); + + $this->mockedEvent->allows()->getOutput()->andReturn($this->mockedOutput); + + $this->subject = new MinimalVersionDecorator( + $this->mockedConstraintToVersionConverter, + $this->mockedComposerInterpreter + ); + } + + protected function tearDown(): void + { + Mockery::close(); + } + + /** + * @test + */ + public function isInstanceOfTerminalCommandDecorator(): void + { + self::assertInstanceOf(TerminalCommandDecorator::class, $this->subject); + } + + /** + * @test + */ + public function skipOnWrongTerminalCommand(): void + { + $mockedTerminalCommand = Mockery::mock(TerminalCommand::class); + $this->mockedEvent->allows()->getTerminalCommand()->andReturn($mockedTerminalCommand); + + $this->mockedTerminalCommand->expects()->setMinimalPhpVersion('7.4.33')->never(); + + $this->subject->decorate($this->mockedEvent); + } + + /** + * @test + */ + public function decorate(): void + { + $this->mockedEvent->allows()->getTerminalCommand()->andReturn($this->mockedTerminalCommand); + $this->mockedComposerInterpreter->expects()->getLocalPhpVersionConstraint()->once()->andReturn('7.4.*'); + + $this->mockedConstraintToVersionConverter->allows() + ->extractActualPhpVersion('7.4.*')->andReturn('7.4.33'); + + $this->mockedTerminalCommand->expects()->setMinimalPhpVersion('7.4.33')->twice(); + $this->mockedOutput->expects()->writeln( + 'Targeted minimal PHP version is 7.4.33' . PHP_EOL, + OutputInterface::VERBOSITY_VERBOSE + )->twice(); + + $this->subject->decorate($this->mockedEvent); + $this->subject->decorate($this->mockedEvent); + } +} diff --git a/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/VersionDecoratorTest.php b/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/VersionDecoratorTest.php index b95dea8..ee6cfc6 100644 --- a/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/VersionDecoratorTest.php +++ b/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/VersionDecoratorTest.php @@ -10,10 +10,11 @@ use Symfony\Component\Console\Output\OutputInterface; use Zooroyal\CodingStandard\CommandLine\ApplicationLifeCycle\ContainerFactory; use Zooroyal\CodingStandard\CommandLine\EnhancedFileInfo\EnhancedFileInfo; -use Zooroyal\CodingStandard\CommandLine\EnhancedFileInfo\EnhancedFileInfoFactory; use Zooroyal\CodingStandard\CommandLine\Environment\Environment; use Zooroyal\CodingStandard\CommandLine\FileSearch\FileSearchInterface; use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\DecorateEvent; +use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\PhpVersion\ComposerInterpreter; +use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\PhpVersion\ConstraintToVersionConverter; use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\PhpVersion\VersionDecorator; use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\PhpVersion\VersionDependentTerminalCommand; use Zooroyal\CodingStandard\CommandLine\StaticCodeAnalysis\Generic\TerminalCommand\TerminalCommand; @@ -21,13 +22,14 @@ class VersionDecoratorTest extends TestCase { + private MockInterface&Environment $mockedEnvironment; + private FileSearchInterface $forgedFileSearch; + private MockInterface&ConstraintToVersionConverter $mockedConstraintToVersionConverter; + private MockInterface&ComposerInterpreter $mockedComposerInterpreter; + private MockInterface&DecorateEvent $mockedEvent; + private MockInterface&VersionDependentTerminalCommand $mockedTerminalCommand; + private MockInterface&OutputInterface $mockedOutput; private VersionDecorator $subject; - private MockInterface|DecorateEvent $mockedEvent; - private MockInterface|Environment $mockedEnvironment; - private MockInterface|VersionDependentTerminalCommand $mockedTerminalCommand; - private MockInterface|OutputInterface $mockedOutput; - private FileSearchInterface|MockInterface $forgedFileSearch; - private EnhancedFileInfoFactory $forgedEnhancedFileInfoFactory; protected function setUp(): void { @@ -37,15 +39,16 @@ protected function setUp(): void $this->mockedOutput = Mockery::mock(OutputInterface::class); $this->forgedFileSearch = ContainerFactory::getUnboundContainerInstance() ->get(FileSearchInterface::class); - $this->forgedEnhancedFileInfoFactory = ContainerFactory::getUnboundContainerInstance() - ->get(EnhancedFileInfoFactory::class); + $this->mockedConstraintToVersionConverter = Mockery::mock(ConstraintToVersionConverter::class); + $this->mockedComposerInterpreter = Mockery::mock(ComposerInterpreter::class); $this->mockedEvent->shouldReceive('getOutput')->andReturn($this->mockedOutput); $this->subject = new VersionDecorator( $this->mockedEnvironment, $this->forgedFileSearch, - $this->forgedEnhancedFileInfoFactory + $this->mockedConstraintToVersionConverter, + $this->mockedComposerInterpreter, ); } @@ -68,9 +71,8 @@ public function isInstanceOfTerminalCommandDecorator(): void public function skipOnWrongTerminalCommand(): void { $mockedTerminalCommand = Mockery::mock(TerminalCommand::class); - $this->mockedEvent->shouldReceive('getTerminalCommand')->once()->andReturn($mockedTerminalCommand); - - $this->mockedTerminalCommand->shouldReceive('setPhpVersion')->never(); + $this->mockedEvent->expects()->getTerminalCommand()->andReturns($mockedTerminalCommand); + $this->mockedTerminalCommand->allows()->setPhpVersion(self::anything())->never(); $this->subject->decorate($this->mockedEvent); } @@ -79,18 +81,8 @@ public function skipOnWrongTerminalCommand(): void public function setVersionsDataProvider(): array { return [ - 'version 7.4' => ['path' => __DIR__ . '/fixture/versions/7.4', 'expectedVersion' => '7.4.0',], - 'version 8.0' => ['path' => __DIR__ . '/fixture/versions/8.0', 'expectedVersion' => '8.0.1',], - 'version 8.1' => ['path' => __DIR__ . '/fixture/versions/8.1', 'expectedVersion' => '8.1.0',], - 'version 8.2' => ['path' => __DIR__ . '/fixture/versions/8.2', 'expectedVersion' => '8.2.0',], - 'version 8.placeholder' => [ - 'path' => __DIR__ . '/fixture/versions/8.placeholder', - 'expectedVersion' => '8.0.0', - ], - 'version none' => ['path' => __DIR__ . '/fixture/versions/none', 'expectedVersion' => '7.4.0',], - 'config' => ['path' => __DIR__ . '/fixture/places/config', 'expectedVersion' => '8.1.0',], - 'require' => ['path' => __DIR__ . '/fixture/places/require', 'expectedVersion' => '8.1.0',], - 'deepSearch' => ['path' => __DIR__ . '/fixture/deepSearch', 'expectedVersion' => '8.0.3',], + 'require' => ['path' => __DIR__ . '/fixture/places/require', 'expectedVersion' => '7.4', 'deep' => false,], + 'deepSearch' => ['path' => __DIR__ . '/fixture/deepSearch', 'expectedVersion' => '8.0.1', 'deep' => true], ]; } @@ -98,15 +90,22 @@ public function setVersionsDataProvider(): array * @test * @dataProvider setVersionsDataProvider */ - public function setVersions(string $path, string $expectedVersion): void + public function setVersions(string $path, string $expectedVersion, bool $deep): void { $mockedEnhancedFileInfo = Mockery::mock(EnhancedFileInfo::class); - $mockedEnhancedFileInfo->expects()->getRealPath()->andReturn($path); - $mockedEnhancedFileInfo->expects()->getPathname()->andReturn($path); + $mockedEnhancedFileInfo->allows()->getRealPath()->andReturn($path); + $mockedEnhancedFileInfo->allows()->getPathname()->andReturn($path); $this->mockedEvent->expects()->getTerminalCommand()->twice()->andReturn($this->mockedTerminalCommand); $this->mockedEnvironment->expects()->getRootDirectory()->once()->andReturn($mockedEnhancedFileInfo); + + $this->mockedComposerInterpreter->allows()->getLocalPhpVersionConstraint()->once()->andReturn('7.4'); + if ($deep) { + $this->mockedConstraintToVersionConverter->expects()->extractActualPhpVersion(self::anything()) + ->twice()->andReturnUsing(static fn(string $parameter) => $parameter . '.1'); + } + $this->mockedTerminalCommand->expects()->setPhpVersion($expectedVersion)->twice(); $this->mockedOutput->expects()->writeln( diff --git a/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/fixture/deepSearch/vendor/dein/paket/composer.json b/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/fixture/deepSearch/vendor/dein/paket/composer.json index 8c45791..fb84810 100644 --- a/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/fixture/deepSearch/vendor/dein/paket/composer.json +++ b/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/fixture/deepSearch/vendor/dein/paket/composer.json @@ -1,10 +1,10 @@ { "require": { - "php": "^8.0.3" + "php": "8.0" }, "config": { "platform": { - "php": "8.2" + "php": "8.1" } } } diff --git a/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/fixture/deepSearch/vendor/mein/paket/composer.json b/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/fixture/deepSearch/vendor/mein/paket/composer.json index 3b92027..5698b26 100644 --- a/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/fixture/deepSearch/vendor/mein/paket/composer.json +++ b/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/fixture/deepSearch/vendor/mein/paket/composer.json @@ -1,10 +1,10 @@ { "require": { - "php": ">=8.0.1 <8.1.0 || ^8.2" + "php": "7.4" }, "config": { "platform": { - "php": "8.1.9" + "php": "8.2" } } } diff --git a/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/fixture/places/config/composer.json b/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/fixture/places/config/composer.json index 12fe17e..573bccc 100644 --- a/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/fixture/places/config/composer.json +++ b/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/fixture/places/config/composer.json @@ -1,7 +1,10 @@ { + "require": { + "php": "8.0" + }, "config": { "platform": { "php": "8.1" } } -} \ No newline at end of file +} diff --git a/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/fixture/places/corrupt/composer.json b/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/fixture/places/corrupt/composer.json new file mode 100644 index 0000000..ffbdb84 --- /dev/null +++ b/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/fixture/places/corrupt/composer.json @@ -0,0 +1,3 @@ +{ + "require": {} + diff --git a/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/fixture/places/none/composer.json b/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/fixture/places/none/composer.json new file mode 100644 index 0000000..6c7f772 --- /dev/null +++ b/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/fixture/places/none/composer.json @@ -0,0 +1,3 @@ +{ + "require": {} +} diff --git a/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/fixture/places/require/composer.json b/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/fixture/places/require/composer.json index 7db0d52..54f09be 100644 --- a/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/fixture/places/require/composer.json +++ b/tests/Unit/CommandLine/StaticCodeAnalysis/Generic/TerminalCommand/PhpVersion/fixture/places/require/composer.json @@ -1,5 +1,5 @@ { "require": { - "php": "8.1" + "php": "8.2" } -} \ No newline at end of file +} diff --git a/tests/Unit/CommandLine/StaticCodeAnalysis/PHPCodeSniffer/TerminalCommandTest.php b/tests/Unit/CommandLine/StaticCodeAnalysis/PHPCodeSniffer/TerminalCommandTest.php index 4fbea28..7182a9a 100644 --- a/tests/Unit/CommandLine/StaticCodeAnalysis/PHPCodeSniffer/TerminalCommandTest.php +++ b/tests/Unit/CommandLine/StaticCodeAnalysis/PHPCodeSniffer/TerminalCommandTest.php @@ -88,7 +88,7 @@ public function terminalCommandCompilation(TerminalCommandTestData $data): void if ($data->getTargets() !== null) { $this->subject->addTargets($data->getTargets()); } - $this->subject->setPhpVersion($data->getPhpVersion()); + $this->subject->setMinimalPhpVersion($data->getPhpVersion()); $result = (string) $this->subject; $resultingArray = $this->subject->toArray();