diff --git a/.github/workflows/testing-suite.yml b/.github/workflows/testing-suite.yml index f042fcf..ee38286 100644 --- a/.github/workflows/testing-suite.yml +++ b/.github/workflows/testing-suite.yml @@ -4,20 +4,28 @@ jobs: PHP: strategy: matrix: - image: [ - 'srcoder/development-php:php74-fpm', - 'srcoder/development-php:php80-fpm', - 'srcoder/development-php:php81-fpm' - ] + php-version: [8.1, 8.2, 8.3, 8.4] runs-on: ubuntu-latest container: - image: ${{ matrix.image }} + image: ${{ matrix.php-version == '8.1' && 'srcoder/development-php:php81-fpm' || + matrix.php-version == '8.2' && 'srcoder/development-php:php82-fpm' || + matrix.php-version == '8.3' && 'srcoder/development-php:php83-fpm' || + matrix.php-version == '8.4' && 'srcoder/development-php:php84-fpm' }} steps: - name: Checkout uses: actions/checkout@v2 - - name: Testing Suite + + - name: Install Dependencies run: | composer2 install --dev --prefer-dist --no-scripts --no-progress --optimize-autoloader --no-interaction -vvv composer2 show - composer2 exec -v grumphp run + shell: bash + + - name: Run GrumPHP Tasks + run: | + if [[ "${{ matrix.php-version }}" == "8.1" || "${{ matrix.php-version }}" == "8.2" ]]; then + composer2 exec -v grumphp -- run --tasks=composer,jsonlint,xmllint,yamllint,phpcs,phplint,phpmd,phpstan,securitychecker_enlightn + else + composer2 exec -v grumphp -- run + fi shell: bash diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cf2274..97e641b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,30 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.0.0] - Unreleased +### Added +- Added `phpunit/phpunit` to suggested dependencies in `composer.json`. +- Added `youwe/coding-standard-phpstorm` to suggested dependencies in `composer.json`. +- Added support to honor upstream version constraints +- Github action for php 8.3 and php 8.4 to run unit tests against PHPUnit 12. +- Testing suite now attempts to install phpunit upstream if it isn't available yet + - Existing upstream versions are honored if already installed + - Upstream projects not having phpunit installed will install phpunit with an @stable version + +### Changed +- Unit tests as part of the testing suite are rewritten for PHPUnit 12 +- Updated GitHub Action workflows to support PHP 8.1, 8.2, and 8.3. +- `composer.json`: Dropped support for PHP < 8.1. +- Moved phpunit from require to require-dev +- Changed PHPMD suppressions in docblocks to quote the rule name, due to changes in later versions of PHPStan that create false positives on these docblocks if not quoted. + +### Removed +- Removed support for EOL PHP versions. Projects running PHP < 8.1 can stick to version 2 of the testing-suite. +- Removed support for Composer 1. Projects still relying on Composer 1 can stick to version 2 of the testing-suite. +- Removed `youwe/coding-standard-phpstorm` as dependency (it is still listed in suggest) +- Removed `phpunit/phpunit` as direct dependency (it is still in require-dev and installed upstream through the `youwe/dependency-installer`) +- Github actions for php < 8.1 + ## 2.19.1 ### Changed - `^0.30` restricts updates to only versions within the `0.30.x` range, preventing upgrades to 0.32.0 for diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a697639..9706ba4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,11 @@ +Before opening a PR with changes, make sure all the linting steps are successful. + +The require-dev dependency for phpunit is set to @stable for the github actions, but the tests themselves\ +assume they are running against PHPUnit 12 and php >= 8.3. The github actions only run phpunit tests against\ +a php 8.3 container. + If a PR is approved please ask one of the following maintainers to get it merged: -[Igor Wulff](https://github.com/igorwulff) -[Dan Wallis](https://github.com/fredden) + +- [Igor Wulff](https://github.com/igorwulff) +- [Leon Helmus](https://github.com/leonhelmus) +- [Rutger Rademakers](https://github.com/rutgerrademaker) diff --git a/composer.json b/composer.json index 3720dee..b5b4582 100644 --- a/composer.json +++ b/composer.json @@ -24,22 +24,25 @@ } ], "require": { - "php": "^7.2 || ^8.0", - "composer-plugin-api": "^1.1 || ^2.0", + "php": "^8.1", + "composer-plugin-api": "^2.0", "enlightn/security-checker": "^1.5 || ^2.0", "kint-php/kint": "@stable", "php-parallel-lint/php-parallel-lint": "^1.2", "phpro/grumphp-shim": "^1.13", "phpstan/phpstan": "@stable", - "phpunit/phpunit": "@stable", "youwe/coding-standard": "^3.5.0", - "youwe/coding-standard-phpstorm": "^2.3.0", "youwe/composer-dependency-installer": "^1.4.0", "youwe/composer-file-installer": "^1.2.0" }, + "suggest": { + "phpunit/phpunit": ">= 9.6", + "youwe/coding-standard-phpstorm": "^2.3.0" + }, "require-dev": { "composer/composer": "@stable", - "mikey179/vfsstream": "@stable" + "mikey179/vfsstream": "@stable", + "phpunit/phpunit": "@stable" }, "replace": { "sensiolabs/security-checker": "*" diff --git a/phpstan.neon b/phpstan.neon index 9d11dfb..35d0f21 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,6 @@ parameters: excludePaths: - src/installers.php + - tests/* ignoreErrors: - '#Property Mediact\\TestingSuite\\Composer\\Installer\\ConfigInstaller::\$io is never read, only written\.#' diff --git a/src/ConfigResolver.php b/src/ConfigResolver.php index 7c2da85..9560402 100644 --- a/src/ConfigResolver.php +++ b/src/ConfigResolver.php @@ -21,11 +21,11 @@ class ConfigResolver * Constructor. * * @param ProjectTypeResolver $typeResolver - * @param string $template + * @param string|null $template */ public function __construct( ProjectTypeResolver $typeResolver, - string $template = null + ?string $template = null, ) { $this->typeResolver = $typeResolver; $this->template = $template ?? $this->template; diff --git a/src/Factory/ProcessFactory.php b/src/Factory/ProcessFactory.php index 99b0429..27ba599 100644 --- a/src/Factory/ProcessFactory.php +++ b/src/Factory/ProcessFactory.php @@ -23,6 +23,7 @@ class ProcessFactory implements ProcessFactoryInterface public function create(string $commandLine): Process { // See https://github.com/composer/composer/blob/1.10.17/src/Composer/Util/ProcessExecutor.php#L68:L72 + // @phpstan-ignore-next-line because phpstan can see it's available, but we cannot guarantee symfony >= 4.2 upstream return method_exists(Process::class, 'fromShellCommandline') ? Process::fromShellCommandline($commandLine) // Symfony >= 4.2 : new Process($commandLine); // Symfony < 4.2 diff --git a/src/Installer/ArchiveExcludeInstaller.php b/src/Installer/ArchiveExcludeInstaller.php index acfe782..1cfd755 100644 --- a/src/Installer/ArchiveExcludeInstaller.php +++ b/src/Installer/ArchiveExcludeInstaller.php @@ -12,12 +12,13 @@ use Composer\Factory; use Composer\IO\IOInterface; use Composer\Json\JsonFile; +use Exception; use Youwe\FileMapping\FileMappingInterface; use Youwe\TestingSuite\Composer\MappingResolver; /** - * @SuppressWarnings(PHPMD.ShortVariable) - * @SuppressWarnings(PHPMD.StaticAccess) + * @SuppressWarnings("PHPMD.ShortVariable") + * @SuppressWarnings("PHPMD.StaticAccess") */ class ArchiveExcludeInstaller implements InstallerInterface { @@ -41,7 +42,7 @@ class ArchiveExcludeInstaller implements InstallerInterface '/.env.dev', '/.gitattributes', '/.gitignore', - '/tests' + '/tests', ]; /** @@ -50,15 +51,15 @@ class ArchiveExcludeInstaller implements InstallerInterface * @param MappingResolver $resolver * @param IOInterface $io * @param JsonFile|null $file - * @param string $destination + * @param string |null $destination * @param array|null $defaults */ public function __construct( MappingResolver $resolver, IOInterface $io, - JsonFile $file = null, - string $destination = null, - array $defaults = null + ?JsonFile $file = null, + ?string $destination = null, + ?array $defaults = null, ) { $this->resolver = $resolver; $this->io = $io; @@ -71,6 +72,7 @@ public function __construct( * Install. * * @return void + * @throws Exception */ public function install(): void { @@ -94,7 +96,7 @@ function (FileMappingInterface $mapping): string { }, iterator_to_array( $this->resolver->resolve() - ) + ), ) ); @@ -107,7 +109,7 @@ function (FileMappingInterface $mapping): string { $this->io->write( sprintf( 'Added: %s to archive exclude in composer.json', - $file + $file, ) ); } diff --git a/src/Installer/ConfigInstaller.php b/src/Installer/ConfigInstaller.php index 3b5dbdd..203261b 100644 --- a/src/Installer/ConfigInstaller.php +++ b/src/Installer/ConfigInstaller.php @@ -10,13 +10,13 @@ namespace Youwe\TestingSuite\Composer\Installer; use Composer\Factory; -use Composer\IO\IOInterface; use Composer\Json\JsonFile; +use Seld\JsonLint\ParsingException; use Youwe\TestingSuite\Composer\ConfigResolver; /** - * @SuppressWarnings(PHPMD.ShortVariable) - * @SuppressWarnings(PHPMD.StaticAccess) + * @SuppressWarnings("PHPMD.ShortVariable") + * @SuppressWarnings("PHPMD.StaticAccess") */ class ConfigInstaller implements InstallerInterface { @@ -34,7 +34,7 @@ class ConfigInstaller implements InstallerInterface */ public function __construct( ConfigResolver $resolver, - JsonFile $file = null + ?JsonFile $file = null, ) { $this->resolver = $resolver; $this->file = $file ?? new JsonFile(Factory::getComposerFile()); @@ -44,6 +44,7 @@ public function __construct( * Install. * * @return void + * @throws ParsingException */ public function install(): void { @@ -52,7 +53,7 @@ public function install(): void $config = array_replace_recursive( $this->resolver->resolve(), - $config + $config, ); $definition['config'] = $config; diff --git a/src/Installer/FilesInstaller.php b/src/Installer/FilesInstaller.php index 2f2da5d..fd80ba8 100644 --- a/src/Installer/FilesInstaller.php +++ b/src/Installer/FilesInstaller.php @@ -15,7 +15,7 @@ use Youwe\TestingSuite\Composer\MappingResolver; /** - * @SuppressWarnings(PHPMD.ShortVariable) + * @SuppressWarnings("PHPMD.ShortVariable") */ class FilesInstaller implements InstallerInterface { @@ -38,7 +38,7 @@ class FilesInstaller implements InstallerInterface public function __construct( MappingResolver $mappingResolver, ComposerFileInstaller $fileInstaller, - IOInterface $io + IOInterface $io, ) { $this->mappingResolver = $mappingResolver; $this->fileInstaller = $fileInstaller; @@ -63,7 +63,7 @@ public function install(): void $this->io->write( sprintf( 'Installed: %s', - $mapping->getRelativeDestination() + $mapping->getRelativeDestination(), ) ); } @@ -72,7 +72,7 @@ public function install(): void /** * @param FileMappingInterface $unixFileMapping * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings("PHPMD.CyclomaticComplexity") * * @return void */ @@ -87,9 +87,9 @@ private function resolveYouwePathing(FileMappingInterface $unixFileMapping): voi [ './vendor/mediact/coding-standard-magento2/src/MediactMagento2', './vendor/mediact/coding-standard/src/MediaCT', - './vendor/youwe/coding-standard-magento2/src/Magento2' + './vendor/youwe/coding-standard-magento2/src/Magento2', ], - 'YouweMagento2' + 'YouweMagento2', ); } elseif ($name === "phpmd.xml") { $this->updatePath( @@ -97,49 +97,55 @@ private function resolveYouwePathing(FileMappingInterface $unixFileMapping): voi [ './vendor/mediact/coding-standard-magento2/src/MediactMagento2/phpmd.xml', './vendor/mediact/coding-standard/src/MediaCT/phpmd.xml', - './vendor/youwe/coding-standard-magento2/src/Magento2/phpmd.xml' + './vendor/youwe/coding-standard-magento2/src/Magento2/phpmd.xml', ], - './vendor/youwe/coding-standard-magento2/src/YouweMagento2/phpmd.xml' + './vendor/youwe/coding-standard-magento2/src/YouweMagento2/phpmd.xml', ); } elseif ($name === "grumphp.yml") { $this->updatePath( $unixFileMapping->getDestination(), [ 'vendor/mediact/testing-suite/config/default/grumphp.yml', - 'vendor/youwe/testing-suite/config/default/grumphp.yml' + 'vendor/youwe/testing-suite/config/default/grumphp.yml', ], - 'vendor/youwe/testing-suite/config/magento2/grumphp.yml' + 'vendor/youwe/testing-suite/config/magento2/grumphp.yml', ); } } elseif ($this->mappingResolver->getTypeResolver()->resolve() === 'magento') { if ($name === "phpcs.xml") { $this->updatePath( $unixFileMapping->getDestination(), - ['./vendor/mediact/coding-standard-magento1/src/MediactMagento1'], - './vendor/youwe/coding-standard-magento1/src/Magento1' + [ + './vendor/mediact/coding-standard-magento1/src/MediactMagento1', + ], + './vendor/youwe/coding-standard-magento1/src/Magento1', ); } } else { if ($name === "phpcs.xml") { $this->updatePath( $unixFileMapping->getDestination(), - ['./vendor/mediact/coding-standard/src/MediaCT'], - './vendor/youwe/coding-standard/src/Global' + [ + './vendor/mediact/coding-standard/src/MediaCT', + ], + './vendor/youwe/coding-standard/src/Global', ); } elseif ($name === "phpmd.xml") { $this->updatePath( $unixFileMapping->getDestination(), [ './vendor/mediact/coding-standard/src/MediaCT/phpmd.xml', - './vendor/youwe/coding-standard-magento2/src/Magento2/phpmd.xml' + './vendor/youwe/coding-standard-magento2/src/Magento2/phpmd.xml', ], - './vendor/youwe/coding-standard/src/Global/phpmd.xml' + './vendor/youwe/coding-standard/src/Global/phpmd.xml', ); } elseif ($name === "grumphp.yml") { $this->updatePath( $unixFileMapping->getDestination(), - ['vendor/mediact/testing-suite/config/default/grumphp.yml'], - 'vendor/youwe/testing-suite/config/default/grumphp.yml' + [ + 'vendor/mediact/testing-suite/config/default/grumphp.yml', + ], + 'vendor/youwe/testing-suite/config/default/grumphp.yml', ); } } @@ -155,13 +161,13 @@ private function resolveYouwePathing(FileMappingInterface $unixFileMapping): voi private function updatePath( string $destination, array $oldPaths, - string $newPath + string $newPath, ): void { $file = file_get_contents($destination); $newFile = str_replace( $oldPaths, $newPath, - $file + $file, ); file_put_contents($destination, $newFile); } diff --git a/src/Installer/PackagesInstaller.php b/src/Installer/PackagesInstaller.php index c3f9258..dced981 100644 --- a/src/Installer/PackagesInstaller.php +++ b/src/Installer/PackagesInstaller.php @@ -16,7 +16,7 @@ use Youwe\TestingSuite\Composer\ProjectTypeResolver; /** - * @SuppressWarnings(PHPMD.ShortVariable) + * @SuppressWarnings("PHPMD.ShortVariable") */ class PackagesInstaller implements InstallerInterface { @@ -33,39 +33,40 @@ class PackagesInstaller implements InstallerInterface private $io; /** @var array */ - private $mapping = [ - MappingResolver::DEFAULT_MAPPING_TYPE => [], + public $mapping = [ + MappingResolver::DEFAULT_MAPPING_TYPE => [ + 'phpunit/phpunit' => [ + 'version' => '@stable', + 'updateDependencies' => true, + 'allowVersionOverride' => false, + ], + ], 'magento1' => [ - [ - 'name' => 'youwe/coding-standard-magento1', + 'youwe/coding-standard-magento1' => [ 'version' => '^1.3.0', - 'dev' => true - ] + 'updateDependencies' => true, + ], ], 'magento2' => [ - [ - 'name' => 'youwe/coding-standard-magento2', + 'youwe/coding-standard-magento2' => [ 'version' => '^2.0.0', - 'dev' => true + 'updateDependencies' => true, ], - [ - 'name' => 'phpstan/extension-installer', + 'phpstan/extension-installer' => [ 'version' => '^1.3', - 'dev' => true + 'updateDependencies' => true, ], - [ - 'name' => 'bitexpert/phpstan-magento', + 'bitexpert/phpstan-magento' => [ 'version' => '~0.30', - 'dev' => true + 'updateDependencies' => true, ], ], 'laravel' => [ - [ - 'name' => 'elgentos/laravel-coding-standard', + 'elgentos/laravel-coding-standard' => [ 'version' => '^1.0.0', - 'dev' => true - ] - ] + 'updateDependencies' => true, + ], + ], ]; /** @@ -81,8 +82,8 @@ public function __construct( Composer $composer, ProjectTypeResolver $typeResolver, IOInterface $io, - DependencyInstaller $installer = null, - array $mapping = null + ?DependencyInstaller $installer = null, + ?array $mapping = null, ) { $this->composer = $composer; $this->typeResolver = $typeResolver; @@ -99,19 +100,21 @@ public function __construct( public function install(): void { $type = $this->typeResolver->resolve(); - if (!isset($this->mapping[$type])) { - return; - } + $projectTypePackages = $this->mapping[$type] ?? []; + $packagesToInstall = array_replace_recursive($this->mapping[MappingResolver::DEFAULT_MAPPING_TYPE], $projectTypePackages); - foreach ($this->mapping[$type] as $package) { - if (!$this->isPackageRequired($package['name'], $package['version'])) { + foreach ($packagesToInstall as $name => $package) { + if (!$this->isPackageRequired($name, $package['version'])) { $this->io->write( - sprintf('Requiring package %s', $package['name']) + sprintf('Requiring package %s', $name) ); $this->installer->installPackage( - $package['name'], - $package['version'] + $name, + $package['version'], + $package['dev'] ?? true, + $package['updateDependencies'] ?? false, + $package['allowVersionOverride'] ?? true, ); } } @@ -121,6 +124,7 @@ public function install(): void * Whether a package has been required. * * @param string $packageName + * @param string $version * * @return bool */ diff --git a/src/MappingResolver.php b/src/MappingResolver.php index 388b0ed..03110b0 100644 --- a/src/MappingResolver.php +++ b/src/MappingResolver.php @@ -40,14 +40,14 @@ public function resolve(): FileMappingReaderInterface __DIR__ . '/../templates/mapping/files', sprintf( __DIR__ . '/../templates/mapping/project/%s', - $this->typeResolver->resolve() - ) + $this->typeResolver->resolve(), + ), ]; return new UnixFileMappingReader( __DIR__ . '/../templates/files', getcwd(), - ...$files + ...$files, ); } diff --git a/src/Plugin.php b/src/Plugin.php index 83d423d..a71f5a2 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -16,7 +16,7 @@ use Youwe\TestingSuite\Composer\Installer\InstallerInterface; /** - * @SuppressWarnings(PHPMD.ShortVariable) + * @SuppressWarnings("PHPMD.ShortVariable") */ class Plugin implements PluginInterface, EventSubscriberInterface { @@ -44,7 +44,7 @@ public function __construct(InstallerInterface ...$installers) public function activate(Composer $composer, IOInterface $io) { $this->addInstallers( - ...include __DIR__ . '/installers.php' + ...include __DIR__ . '/installers.php', ); } @@ -105,11 +105,11 @@ public static function getSubscribedEvents(): array { return [ 'post-install-cmd' => [ - 'install' + 'install', ], 'post-update-cmd' => [ - 'install' - ] + 'install', + ], ]; } } diff --git a/src/ProjectTypeResolver.php b/src/ProjectTypeResolver.php index 40ad900..b6eb66b 100644 --- a/src/ProjectTypeResolver.php +++ b/src/ProjectTypeResolver.php @@ -21,7 +21,7 @@ class ProjectTypeResolver */ public const COMPOSER_CONFIG_KEYS = [ 'youwe-testing-suite', - 'mediact-testing-suite' + 'mediact-testing-suite', ]; /** @@ -40,7 +40,7 @@ class ProjectTypeResolver 'magento-project' => 'magento2', 'alumio-project' => 'alumio', 'laravel-project' => 'laravel', - 'pimcore-project' => 'pimcore' + 'pimcore-project' => 'pimcore', ]; public const DEFAULT_PROJECT_TYPE = 'default'; @@ -51,7 +51,7 @@ class ProjectTypeResolver * @param Composer $composer * @param array|null $mapping */ - public function __construct(Composer $composer, array $mapping = null) + public function __construct(Composer $composer, ?array $mapping = null) { $this->composer = $composer; $this->mapping = $mapping ?? $this->mapping; diff --git a/src/installers.php b/src/installers.php index d322159..d9a1e64 100644 --- a/src/installers.php +++ b/src/installers.php @@ -27,7 +27,7 @@ $mappingResolver = new MappingResolver($typeResolver); $configResolver = new ConfigResolver($typeResolver); $fileInstaller = new FileInstaller( - new UnixFileMappingReader('', '') + new UnixFileMappingReader('', ''), ); $processFactory = new ProcessFactory(); @@ -35,5 +35,5 @@ new FilesInstaller($mappingResolver, $fileInstaller, $io), new ArchiveExcludeInstaller($mappingResolver, $io), new PackagesInstaller($composer, $typeResolver, $io), - new ConfigInstaller($configResolver) + new ConfigInstaller($configResolver), ]; diff --git a/tests/ConfigResolverTest.php b/tests/ConfigResolverTest.php index b43fb6a..938901b 100644 --- a/tests/ConfigResolverTest.php +++ b/tests/ConfigResolverTest.php @@ -10,21 +10,21 @@ namespace Youwe\TestingSuite\Composer\Tests; use org\bovigo\vfs\vfsStream; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\MockObject\Exception; use PHPUnit\Framework\TestCase; use Youwe\TestingSuite\Composer\ConfigResolver; use Youwe\TestingSuite\Composer\ProjectTypeResolver; /** - * @coversDefaultClass \Youwe\TestingSuite\Composer\ConfigResolver - * @SuppressWarnings(PHPMD) + * @phpcs:disable GlobalPhpUnit.Coverage.CoversTag.CoversTagMissing */ +#[CoversMethod(ConfigResolver::class, '__construct')] +#[CoversMethod(ConfigResolver::class, 'resolve')] class ConfigResolverTest extends TestCase { /** - * @return void - * - * @covers ::__construct - * @covers ::resolve + * @throws Exception */ public function testResolve(): void { @@ -33,7 +33,7 @@ public function testResolve(): void $filesystem = vfsStream::setup( sha1(__METHOD__), null, - [$jsonFile => $jsonData] + [$jsonFile => $jsonData], ); $template = $filesystem->url() . '/%s.json'; diff --git a/tests/Factory/ProcessFactoryTest.php b/tests/Factory/ProcessFactoryTest.php index fcf0464..6d84b75 100644 --- a/tests/Factory/ProcessFactoryTest.php +++ b/tests/Factory/ProcessFactoryTest.php @@ -9,27 +9,24 @@ namespace Youwe\TestingSuite\Composer\Tests\Factory; +use PHPUnit\Framework\Attributes\CoversMethod; use PHPUnit\Framework\TestCase; use Symfony\Component\Process\Process; use Youwe\TestingSuite\Composer\Factory\ProcessFactory; /** - * @coversDefaultClass \Youwe\TestingSuite\Composer\Factory\ProcessFactory + * @phpcs:disable GlobalPhpUnit.Coverage.CoversTag.CoversTagMissing */ +#[CoversMethod(ProcessFactory::class, 'create')] class ProcessFactoryTest extends TestCase { - /** - * @return void - * - * @covers ::create - */ - public function testCreate() + public function testCreate(): void { $factory = new ProcessFactory(); $this->assertInstanceOf( Process::class, - $factory->create('foo') + $factory->create('foo'), ); } } diff --git a/tests/Installer/ArchiveExcludeInstallerTest.php b/tests/Installer/ArchiveExcludeInstallerTest.php index 241fec1..93136ad 100644 --- a/tests/Installer/ArchiveExcludeInstallerTest.php +++ b/tests/Installer/ArchiveExcludeInstallerTest.php @@ -13,7 +13,9 @@ use Composer\Json\JsonFile; use org\bovigo\vfs\vfsStream; use org\bovigo\vfs\vfsStreamDirectory; -use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\MockObject\Exception; use PHPUnit\Framework\TestCase; use Youwe\FileMapping\FileMappingInterface; use Youwe\FileMapping\FileMappingReaderInterface; @@ -21,32 +23,23 @@ use Youwe\TestingSuite\Composer\MappingResolver; /** - * @coversDefaultClass \Youwe\TestingSuite\Composer\Installer\ArchiveExcludeInstaller - * @SuppressWarnings(PHPMD) + * @phpcs:disable GlobalPhpUnit.Coverage.CoversTag.CoversTagMissing */ +#[CoversMethod(ArchiveExcludeInstaller::class, '__construct')] +#[CoversMethod(ArchiveExcludeInstaller::class, 'install')] class ArchiveExcludeInstallerTest extends TestCase { /** - * @param array $existingFiles - * @param array $files - * @param array $defaults - * @param array $definition - * @param array $expected - * - * @return void - * - * @dataProvider dataProvider - * - * @covers ::__construct - * @covers ::install + * @throws Exception */ + #[DataProvider('dataProvider')] public function testInstall( array $existingFiles, array $files, array $defaults, array $definition, - array $expected - ) { + array $expected, + ): void { $file = $this->createMock(JsonFile::class); $resolver = $this->createMock(MappingResolver::class); $io = $this->createMock(IOInterface::class); @@ -73,39 +66,36 @@ public function testInstall( $io, $file, $filesystem->url(), - $defaults + $defaults, ); $installer->install(); } - /** - * @return array - */ - public function dataProvider(): array + public static function dataProvider(): array { return [ [ [ 'foo-file.txt', 'bar-file.txt', - 'default.txt' + 'default.txt', ], [ 'foo-file.txt', 'bar-file.txt', - 'baz-file.txt' + 'baz-file.txt', ], [ '/default.txt', - '/other-default.txt' + '/other-default.txt', ], [ 'archive' => [ 'exclude' => [ - 'existing.txt' - ] - ] + 'existing.txt', + ], + ], ], [ 'archive' => [ @@ -113,22 +103,19 @@ public function dataProvider(): array '/existing.txt', '/default.txt', '/foo-file.txt', - '/bar-file.txt' - ] - ] - ] - ] + '/bar-file.txt', + ], + ], + ], + ], ]; } /** - * @param array $files - * - * @return FileMappingReaderInterface + * @throws Exception */ private function createReaderMock(array $files): FileMappingReaderInterface { - /** @var FileMappingReaderInterface|MockObject $mock */ $mock = $this->createMock(FileMappingReaderInterface::class); $valids = array_fill(0, count($files), true); @@ -136,7 +123,6 @@ private function createReaderMock(array $files): FileMappingReaderInterface $mappings = array_map( function (string $file): FileMappingInterface { - /** @var FileMappingInterface|MockObject $mapping */ $mapping = $this->createMock(FileMappingInterface::class); $mapping ->expects(self::any()) @@ -166,11 +152,6 @@ function (string $file): FileMappingInterface { return $mock; } - /** - * @param array $files - * - * @return vfsStreamDirectory - */ private function createFilesystem(array $files): vfsStreamDirectory { return vfsStream::setup( diff --git a/tests/Installer/ConfigInstallerTest.php b/tests/Installer/ConfigInstallerTest.php index 70b3937..341322b 100644 --- a/tests/Installer/ConfigInstallerTest.php +++ b/tests/Installer/ConfigInstallerTest.php @@ -9,23 +9,22 @@ namespace Youwe\TestingSuite\Composer\Tests\Installer; -use Composer\IO\IOInterface; use Composer\Json\JsonFile; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\MockObject\Exception; use PHPUnit\Framework\TestCase; use Youwe\TestingSuite\Composer\ConfigResolver; use Youwe\TestingSuite\Composer\Installer\ConfigInstaller; /** - * @coversDefaultClass \Youwe\TestingSuite\Composer\Installer\ConfigInstaller - * @SuppressWarnings(PHPMD) + * @phpcs:disable GlobalPhpUnit.Coverage.CoversTag.CoversTagMissing */ +#[CoversMethod(ConfigInstaller::class, '__construct')] +#[CoversMethod(ConfigInstaller::class, 'install')] class ConfigInstallerTest extends TestCase { /** - * @return void - * - * @covers ::__construct - * @covers ::install + * @throws Exception */ public function testInstall(): void { @@ -35,11 +34,11 @@ public function testInstall(): void $installer = new ConfigInstaller($resolver, $file); $resolverOutput = [ - 'sort-packages' => true + 'sort-packages' => true, ]; $configWrite = [ - 'config' => $resolverOutput + 'config' => $resolverOutput, ]; $file @@ -70,7 +69,7 @@ public function dataProvider(): array [], [ 'sort-packages' => true - ] + ], ], [ [], @@ -78,10 +77,10 @@ public function dataProvider(): array 'extra' => [ 'grumphp' => [ 'config-default-path' => 'vendor/youwe/testing-suite/config/default/grumphp.yml' - ] - ] - ] - ] + ], + ], + ], + ], ]; } } diff --git a/tests/Installer/FilesInstallerTest.php b/tests/Installer/FilesInstallerTest.php index 545c7d0..334f58c 100644 --- a/tests/Installer/FilesInstallerTest.php +++ b/tests/Installer/FilesInstallerTest.php @@ -12,7 +12,9 @@ use Composer\IO\IOInterface; use org\bovigo\vfs\vfsStream; use org\bovigo\vfs\vfsStreamDirectory; -use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\MockObject\Exception; use PHPUnit\Framework\TestCase; use Youwe\Composer\FileInstaller; use Youwe\FileMapping\FileMappingInterface; @@ -21,27 +23,21 @@ use Youwe\TestingSuite\Composer\MappingResolver; /** - * @coversDefaultClass FilesInstaller - * @SuppressWarnings(PHPMD) + * @phpcs:disable GlobalPhpUnit.Coverage.CoversTag.CoversTagMissing */ +#[CoversMethod(FileInstaller::class, '__construct')] +#[CoversMethod(FileInstaller::class, 'install')] class FilesInstallerTest extends TestCase { /** - * @param array $existingFiles - * @param array $files - * @param int $expectedInstalls - * - * @return void - * @dataProvider dataProvider - * - * @covers ::__construct - * @covers ::install + * @throws Exception */ + #[DataProvider('dataProvider')] public function testInstall( array $existingFiles, array $files, - int $expectedInstalls - ) { + int $expectedInstalls, + ): void { $filesystem = $this->createFilesystem($existingFiles); $reader = $this->createReaderMock($files, $filesystem->url()); $resolver = $this->createMock(MappingResolver::class); @@ -61,10 +57,7 @@ public function testInstall( $installer->install(); } - /** - * @return array - */ - public function dataProvider(): array + public static function dataProvider(): array { return [ [ @@ -74,22 +67,18 @@ public function dataProvider(): array [ 'foo-file.txt', 'bar-file.txt', - 'baz-file.txt' + 'baz-file.txt', ], 2 - ] + ], ]; } /** - * @param array $files - * @param string $destination - * - * @return FileMappingReaderInterface + * @throws Exception */ private function createReaderMock(array $files, string $destination): FileMappingReaderInterface { - /** @var FileMappingReaderInterface|MockObject $mock */ $mock = $this->createMock(FileMappingReaderInterface::class); $valids = array_fill(0, count($files), true); @@ -97,7 +86,6 @@ private function createReaderMock(array $files, string $destination): FileMappin $mappings = array_map( function (string $file) use ($destination): FileMappingInterface { - /** @var FileMappingInterface|MockObject $mapping */ $mapping = $this->createMock(FileMappingInterface::class); $mapping ->expects(self::any()) @@ -127,17 +115,12 @@ function (string $file) use ($destination): FileMappingInterface { return $mock; } - /** - * @param array $files - * - * @return vfsStreamDirectory - */ private function createFilesystem(array $files): vfsStreamDirectory { return vfsStream::setup( sha1(__METHOD__), null, - array_map('strval', array_flip($files)) + array_map('strval', array_flip($files)), ); } } diff --git a/tests/Installer/PackagesInstallerTest.php b/tests/Installer/PackagesInstallerTest.php index 9e8210f..2d76d6a 100644 --- a/tests/Installer/PackagesInstallerTest.php +++ b/tests/Installer/PackagesInstallerTest.php @@ -11,121 +11,189 @@ use Composer\Composer; use Composer\IO\IOInterface; -use Composer\Package\Link; -use Composer\Package\Package; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\MockObject\Exception; use PHPUnit\Framework\TestCase; use Youwe\Composer\DependencyInstaller\DependencyInstaller; use Youwe\TestingSuite\Composer\Installer\PackagesInstaller; +use Youwe\TestingSuite\Composer\MappingResolver; use Youwe\TestingSuite\Composer\ProjectTypeResolver; /** - * @coversDefaultClass \Youwe\TestingSuite\Composer\Installer\PackagesInstaller - * @SuppressWarnings(PHPMD) + * @phpcs:disable GlobalPhpUnit.Coverage.CoversTag.CoversTagMissing */ +#[CoversMethod(PackagesInstaller::class, '__construct')] +#[CoversMethod(PackagesInstaller::class, 'install')] +#[CoversMethod(PackagesInstaller::class, 'isPackageRequired')] class PackagesInstallerTest extends TestCase { /** - * @param string $type - * @param array $requires - * @param array|null $expected - * - * @return void - * @dataProvider dataProvider - * - * @covers ::__construct - * @covers ::install - * @covers ::isPackageRequired + * @throws Exception */ - public function testInstall( - string $type, - array $requires, - array $expected = null - ) { + public function testCanInstallWithMocks(): void + { $composer = $this->createMock(Composer::class); $typeResolver = $this->createMock(ProjectTypeResolver::class); $depInstaller = $this->createMock(DependencyInstaller::class); $io = $this->createMock(IOInterface::class); - $typeResolver - ->expects(self::any()) - ->method('resolve') - ->willReturn($type); - $installer = new PackagesInstaller( $composer, $typeResolver, $io, - $depInstaller + $depInstaller, ); - if ($expected) { - $depInstaller - ->expects(self::exactly(count($expected))) - ->method('installPackage') - ->withConsecutive(...$expected); - } else { - $depInstaller - ->expects(self::never()) - ->method('installPackage'); - } + $depInstaller + ->expects($this->atLeastOnce()) + ->method('installPackage'); $installer->install(); } /** - * @return array + * @throws Exception */ - public function dataProvider(): array + public function testCanMergeDefaultPackagesWhenInstalling(): void { - return [ - [ - 'magento1', - $this->createLinkMocks(['foo/bar']), - [['youwe/coding-standard-magento1']] + $composer = $this->createMock(Composer::class); + $typeResolver = $this->createMock(ProjectTypeResolver::class); + $depInstaller = $this->createMock(DependencyInstaller::class); + $io = $this->createMock(IOInterface::class); + + // Simulate magento 1 project + $typeResolver + ->method('resolve') + ->willReturn('magento1'); + + $mapping = [ + MappingResolver::DEFAULT_MAPPING_TYPE => [ + 'phpunit/phpunit' => [ + 'version' => '@stable', + 'dev' => true, + 'updateDependencies' => true, + 'allowVersionOverride' => false, + ], ], - [ - 'magento1', - $this->createLinkMocks( - ['foo/bar', 'youwe/coding-standard-magento1'] - ), - [['youwe/coding-standard-magento1']] + 'magento1' => [ + 'youwe/coding-standard-magento1' => [ + 'version' => '^1.3.0', + 'dev' => true, + 'updateDependencies' => false, + 'allowVersionOverride' => true, + ], ], - [ - 'magento2', - $this->createLinkMocks(['foo/bar']), - [['youwe/coding-standard-magento2']] + ]; + + $installer = new PackagesInstaller( + $composer, + $typeResolver, + $io, + $depInstaller, + $mapping + ); + + $depInstaller + ->expects($this->exactly(2)) + ->method('installPackage') + ->willReturnCallback( + function ($package, $version, $dev, $updateDependencies, $allowVersionOverride) { + static $calls = 0; + $calls++; + + if ($calls === 1) { + $this->assertEquals('phpunit/phpunit', $package); + $this->assertEquals('@stable', $version); + $this->assertTrue($dev); + $this->assertTrue($updateDependencies); + $this->assertFalse($allowVersionOverride); + } + + if ($calls === 2) { + $this->assertEquals('youwe/coding-standard-magento1', $package); + $this->assertEquals('^1.3.0', $version); + $this->assertTrue($dev); + $this->assertFalse($updateDependencies); + $this->assertTrue($allowVersionOverride); + } + + if ($calls > 2) { + $this->fail('Unexpected number of calls'); + } + } + ); + + $installer->install(); + } + + public function testCanMergeRecursivelyWhenInstalling(): void + { + $composer = $this->createMock(Composer::class); + $typeResolver = $this->createMock(ProjectTypeResolver::class); + $depInstaller = $this->createMock(DependencyInstaller::class); + $io = $this->createMock(IOInterface::class); + + $mapping = [ + MappingResolver::DEFAULT_MAPPING_TYPE => [ + 'phpunit/phpunit' => [ + 'version' => '@stable', + 'dev' => true, + 'updateDependencies' => true, + 'allowVersionOverride' => false, + ], ], - [ - 'default', - $this->createLinkMocks(['foo/bar']), - null + 'magento2' => [ + 'phpunit/phpunit' => [ + 'version' => '^10.6.5', + 'dev' => false, + 'allowVersionOverride' => true, + ], ], - [ - 'unknown', - $this->createLinkMocks(['foo/bar']), - null - ] ]; + + $typeResolver + ->method('resolve') + ->willReturn('magento2'); + + $installer = new PackagesInstaller( + $composer, + $typeResolver, + $io, + $depInstaller, + $mapping, + ); + + $depInstaller + ->expects($this->exactly(1)) + ->method('installPackage') + ->with('phpunit/phpunit', '^10.6.5', false, true, true); + + $installer->install(); } - /** - * @param string[] $targets - * - * @return Link[] - */ - private function createLinkMocks(array $targets): array + public function testPhpUnitIsInstalledForUnknownProjectType(): void { - return array_map( - function (string $target): Link { - /** @var Link $mock */ - $mock = $this->createConfiguredMock( - Link::class, - ['getTarget' => $target] - ); - - return $mock; - }, - $targets + $composer = $this->createMock(Composer::class); + $typeResolver = $this->createMock(ProjectTypeResolver::class); + $depInstaller = $this->createMock(DependencyInstaller::class); + $io = $this->createMock(IOInterface::class); + + $typeResolver + ->method('resolve') + ->willReturn('foobar'); + + $installer = new PackagesInstaller( + $composer, + $typeResolver, + $io, + $depInstaller, ); + + $depInstaller + ->expects($this->exactly(1)) + ->method('installPackage') + ->with('phpunit/phpunit', '@stable', true, true, false); + + $installer->install(); } } diff --git a/tests/MappingResolverTest.php b/tests/MappingResolverTest.php index 10014d1..ce46631 100644 --- a/tests/MappingResolverTest.php +++ b/tests/MappingResolverTest.php @@ -9,21 +9,22 @@ namespace Youwe\TestingSuite\Composer\Tests; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\MockObject\Exception; use PHPUnit\Framework\TestCase; use Youwe\FileMapping\FileMappingReaderInterface; use Youwe\TestingSuite\Composer\MappingResolver; use Youwe\TestingSuite\Composer\ProjectTypeResolver; /** - * @coversDefaultClass \Youwe\TestingSuite\Composer\MappingResolver + * @phpcs:disable GlobalPhpUnit.Coverage.CoversTag.CoversTagMissing */ +#[CoversMethod(MappingResolver::class, '__construct')] +#[CoversMethod(MappingResolver::class, 'resolve')] class MappingResolverTest extends TestCase { /** - * @return void - * - * @covers ::__construct - * @covers ::resolve + * @throws Exception */ public function testResolve() { diff --git a/tests/PluginTest.php b/tests/PluginTest.php index 826b004..56c7082 100644 --- a/tests/PluginTest.php +++ b/tests/PluginTest.php @@ -11,29 +11,30 @@ use Composer\Composer; use Composer\IO\IOInterface; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\MockObject\Exception; use PHPUnit\Framework\TestCase; use ReflectionProperty; use Youwe\TestingSuite\Composer\Installer\InstallerInterface; use Youwe\TestingSuite\Composer\Plugin; /** - * @coversDefaultClass \Youwe\TestingSuite\Composer\Plugin - * @SuppressWarnings(PHPMD) + * @phpcs:disable GlobalPhpUnit.Coverage.CoversTag.CoversTagMissing */ +#[CoversMethod(Plugin::class, 'activate')] +#[CoversMethod(Plugin::class, 'install')] +#[CoversMethod(Plugin::class, 'getSubscribedEvents')] class PluginTest extends TestCase { /** - * @return void - * - * @covers ::activate - * @covers ::addInstallers + * @throws Exception */ - public function testActivate() + public function testActivate(): void { $plugin = new Plugin(); $plugin->activate( $this->createMock(Composer::class), - $this->createMock(IOInterface::class) + $this->createMock(IOInterface::class), ); $reflection = new ReflectionProperty(Plugin::class, 'installers'); @@ -41,17 +42,14 @@ public function testActivate() $this->assertContainsOnlyInstancesOf( InstallerInterface::class, - $reflection->getValue($plugin) + $reflection->getValue($plugin), ); } /** - * @return void - * - * @covers ::__construct - * @covers ::install + * @throws Exception */ - public function testInstall() + public function testInstall(): void { $installers = [ $this->createMock(InstallerInterface::class), @@ -68,12 +66,7 @@ public function testInstall() $plugin->install(); } - /** - * @return void - * - * @covers ::getSubscribedEvents - */ - public function testGetSubscribesEvents() + public function testGetSubscribesEvents(): void { $plugin = new Plugin(); diff --git a/tests/ProjectTypeResolverTest.php b/tests/ProjectTypeResolverTest.php index 01b2459..5221b89 100644 --- a/tests/ProjectTypeResolverTest.php +++ b/tests/ProjectTypeResolverTest.php @@ -12,25 +12,23 @@ use Composer\Composer; use Composer\Config; use Composer\Package\RootPackageInterface; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\TestWith; +use PHPUnit\Framework\MockObject\Exception; use PHPUnit\Framework\TestCase; use Youwe\TestingSuite\Composer\ProjectTypeResolver; -/** - * @coversDefaultClass \Youwe\TestingSuite\Composer\ProjectTypeResolver - */ +#[CoversMethod(ProjectTypeResolver::class, '__construct')] +#[CoversMethod(ProjectTypeResolver::class, 'resolve')] class ProjectTypeResolverTest extends TestCase { /** - * @param string $packageType - * @param string $expected - * - * @return void - * - * @dataProvider dataProvider - * - * @covers ::__construct - * @covers ::resolve + * @throws Exception */ + #[TestWith(['some-type', 'default'])] + #[TestWith(['magento-module', 'magento1'])] + #[TestWith(['magento2-module', 'magento2'])] + #[TestWith(['alumio-project', 'alumio'])] public function testToString(string $packageType, string $expected): void { $composer = $this->createMock(Composer::class); @@ -56,12 +54,6 @@ public function testToString(string $packageType, string $expected): void $this->assertEquals($expected, $decider->resolve()); } - /** - * @return void - * - * @covers ::__construct - * @covers ::resolve - */ public function testToStringOverwrite(): void { $composer = $this->createMock(Composer::class); @@ -91,17 +83,4 @@ public function testToStringOverwrite(): void $decider = new ProjectTypeResolver($composer); $this->assertEquals('magento2', $decider->resolve()); } - - /** - * @return array - */ - public function dataProvider(): array - { - return [ - ['some-type', 'default'], - ['magento-module', 'magento1'], - ['magento2-module', 'magento2'], - ['alumio-project', 'alumio'], - ]; - } }