From 3cfadee2bb960457d4c1e4e9cd51faff309808ff Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Fri, 7 Aug 2020 11:23:45 +0100 Subject: [PATCH] PHP 8 and PHPUnit 9.3 support --- .github/workflows/static.yml | 4 +-- .github/workflows/tests.yml | 34 ++++++++++++-------------- composer.json | 7 +++--- phpstan.neon | 1 + src/Actions/ValidatesConfiguration.php | 7 ++---- src/Factories/TestCaseFactory.php | 11 +++++++-- src/PendingObjects/TestCall.php | 7 ++++++ src/Support/Container.php | 15 ++++++------ src/Support/HigherOrderMessage.php | 11 ++++++++- src/Support/Reflection.php | 30 +++++++++++++++++++++++ tests/.snapshots/success.txt | 16 ++++++------ 11 files changed, 95 insertions(+), 48 deletions(-) diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index 0f54227d..e3860614 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -20,7 +20,7 @@ jobs: coverage: none - name: Install Dependencies - run: composer update --no-interaction --prefer-dist --no-progress + run: composer update --no-interaction --no-progress - name: Run Rector run: vendor/bin/rector process src --dry-run @@ -48,7 +48,7 @@ jobs: coverage: none - name: Install Dependencies - run: composer update --prefer-stable --no-interaction --prefer-dist --no-progress + run: composer update --prefer-stable --no-interaction --no-progress - name: Run PHPStan run: vendor/bin/phpstan analyse --no-progress diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index dfb07edc..883e3fd6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -6,43 +6,39 @@ jobs: ci: runs-on: ${{ matrix.os }} strategy: - fail-fast: true matrix: os: [ubuntu-latest, macos-latest, windows-latest] - php: [7.3, 7.4] + php: ['7.3', '7.4', '8.0'] dependency-version: [prefer-lowest, prefer-stable] - name: Tests P${{ matrix.php }} - ${{ matrix.os }} - ${{ matrix.dependency-version }} + name: PHP ${{ matrix.php }} - ${{ matrix.os }} - ${{ matrix.dependency-version }} steps: - - name: Checkout uses: actions/checkout@v2 - - name: Cache dependencies - uses: actions/cache@v1 - with: - path: ~/.composer/cache/files - key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} - - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} - extensions: dom, mbstring, zip + tools: composer:v2 coverage: none - - name: Install Composer dependencies - run: composer update --${{ matrix.dependency-version }} --no-interaction --prefer-dist + - name: Setup Problem Matches + run: | + echo "::add-matcher::${{ runner.tool_cache }}/php.json" + echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: Install PHP 7 dependencies + run: composer update --${{ matrix.dependency-version }} --no-interaction --no-progress + if: "matrix.php < 8" + + - name: Install PHP 8 dependencies + run: composer update --${{ matrix.dependency-version }} --ignore-platform-req=php --no-interaction --no-progress + if: "matrix.php >= 8" - name: Unit Tests run: php bin/pest --colors=always --exclude-group=integration - name: Integration Tests run: php bin/pest --colors=always --group=integration - - - name: Setup problem matchers for PHP - run: echo "::add-matcher::${{ runner.tool_cache }}/php.json" - - - name: Setup problem matchers for Pest - run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" diff --git a/composer.json b/composer.json index 837e6f11..9907d7e4 100644 --- a/composer.json +++ b/composer.json @@ -17,13 +17,12 @@ } ], "require": { - "php": "^7.3", + "php": "^7.3 || ^8.0", "nunomaduro/collision": "^5.0.0-BETA4", "pestphp/pest-plugin": "^0.3", "pestphp/pest-plugin-coverage": "^0.3", "pestphp/pest-plugin-init": "^0.3", - "phpunit/phpunit": "^9.2.6", - "sebastian/environment": "^5.1.2" + "phpunit/phpunit": "9.3.2" }, "autoload": { "psr-4": { @@ -45,7 +44,7 @@ "require-dev": { "illuminate/console": "^7.16.1", "illuminate/support": "^7.16.1", - "mockery/mockery": "^1.4.0", + "mockery/mockery": "^1.4.1", "pestphp/pest-dev-tools": "dev-master" }, "minimum-stability": "dev", diff --git a/phpstan.neon b/phpstan.neon index e133d425..6900f634 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -21,3 +21,4 @@ parameters: - "# with null as default value#" - "#has parameter \\$closure with default value.#" - "#has parameter \\$description with default value.#" + - "#Method Pest\\\\Support\\\\Reflection::getParameterClassName\\(\\) has a nullable return type declaration.#" diff --git a/src/Actions/ValidatesConfiguration.php b/src/Actions/ValidatesConfiguration.php index a2a6a481..1cbb19a9 100644 --- a/src/Actions/ValidatesConfiguration.php +++ b/src/Actions/ValidatesConfiguration.php @@ -6,8 +6,7 @@ namespace Pest\Actions; use Pest\Exceptions\AttributeNotSupportedYet; use Pest\Exceptions\FileOrFolderNotFound; -use PHPUnit\TextUI\Configuration\Configuration; -use PHPUnit\TextUI\Configuration\Registry; +use PHPUnit\TextUI\XmlConfiguration\Loader; /** * @internal @@ -30,9 +29,7 @@ final class ValidatesConfiguration throw new FileOrFolderNotFound('phpunit.xml'); } - $configuration = Registry::getInstance() - ->get($arguments[self::CONFIGURATION_KEY]) - ->phpunit(); + $configuration = (new Loader())->load($arguments[self::CONFIGURATION_KEY])->phpunit(); if ($configuration->processIsolation()) { throw new AttributeNotSupportedYet('processIsolation', 'true'); diff --git a/src/Factories/TestCaseFactory.php b/src/Factories/TestCaseFactory.php index f3e8e282..06a2a6fb 100644 --- a/src/Factories/TestCaseFactory.php +++ b/src/Factories/TestCaseFactory.php @@ -157,8 +157,15 @@ final class TestCaseFactory } /** - * Makes a fully qualified class name - * from the given filename. + * Makes a fully qualified class name from the current filename. + */ + public function getClassName(): string + { + return $this->makeClassFromFilename($this->filename); + } + + /** + * Makes a fully qualified class name from the given filename. */ public function makeClassFromFilename(string $filename): string { diff --git a/src/PendingObjects/TestCall.php b/src/PendingObjects/TestCall.php index 613a4a97..34aae3fc 100644 --- a/src/PendingObjects/TestCall.php +++ b/src/PendingObjects/TestCall.php @@ -9,6 +9,7 @@ use Pest\Factories\TestCaseFactory; use Pest\Support\Backtrace; use Pest\Support\NullClosure; use Pest\TestSuite; +use PHPUnit\Framework\ExecutionOrderDependency; use SebastianBergmann\Exporter\Exporter; /** @@ -91,6 +92,12 @@ final class TestCall */ public function depends(string ...$tests): TestCall { + $className = $this->testCaseFactory->getClassName(); + + $tests = array_map(function (string $test) use ($className): ExecutionOrderDependency { + return ExecutionOrderDependency::createFromDependsAnnotation($className, $test); + }, $tests); + $this->testCaseFactory ->factoryProxies ->add(Backtrace::file(), Backtrace::line(), 'setDependencies', [$tests]); diff --git a/src/Support/Container.php b/src/Support/Container.php index fa45f500..42175289 100644 --- a/src/Support/Container.php +++ b/src/Support/Container.php @@ -75,14 +75,15 @@ final class Container if ($constructor !== null) { $params = array_map( function (ReflectionParameter $param) use ($id) { - $candidate = null; + $candidate = Reflection::getParameterClassName($param); - if ($param->getType() !== null && $param->getType()->isBuiltin()) { - $candidate = $param->getName(); - } elseif ($param->getClass() !== null) { - $candidate = $param->getClass()->getName(); - } else { - throw ShouldNotHappen::fromMessage(sprintf('The type of `$%s` in `%s` cannot be determined.', $id, $param->getName())); + if ($candidate === null) { + $type = $param->getType(); + if ($type !== null && $type->isBuiltin()) { + $candidate = $param->getName(); + } else { + throw ShouldNotHappen::fromMessage(sprintf('The type of `$%s` in `%s` cannot be determined.', $id, $param->getName())); + } } return $this->get($candidate); diff --git a/src/Support/HigherOrderMessage.php b/src/Support/HigherOrderMessage.php index e5e4e21b..75e351b6 100644 --- a/src/Support/HigherOrderMessage.php +++ b/src/Support/HigherOrderMessage.php @@ -76,7 +76,7 @@ final class HigherOrderMessage Reflection::setPropertyValue($throwable, 'file', $this->filename); Reflection::setPropertyValue($throwable, 'line', $this->line); - if ($throwable->getMessage() === sprintf(self::UNDEFINED_METHOD, $this->methodName)) { + if ($throwable->getMessage() === self::getUndefinedMethodMessage($target, $this->methodName)) { /** @var \ReflectionClass $reflection */ $reflection = new ReflectionClass($target); /* @phpstan-ignore-next-line */ @@ -87,4 +87,13 @@ final class HigherOrderMessage throw $throwable; } } + + private static function getUndefinedMethodMessage(object $target, string $methodName): string + { + if (\PHP_MAJOR_VERSION >= 8) { + return sprintf(sprintf(self::UNDEFINED_METHOD, sprintf('%s::%s()', get_class($target), $methodName))); + } + + return sprintf(self::UNDEFINED_METHOD, $methodName); + } } diff --git a/src/Support/Reflection.php b/src/Support/Reflection.php index 44805ba2..984cfef6 100644 --- a/src/Support/Reflection.php +++ b/src/Support/Reflection.php @@ -9,6 +9,8 @@ use Pest\Exceptions\ShouldNotHappen; use ReflectionClass; use ReflectionException; use ReflectionFunction; +use ReflectionNamedType; +use ReflectionParameter; use ReflectionProperty; /** @@ -117,4 +119,32 @@ final class Reflection $reflectionProperty->setAccessible(true); $reflectionProperty->setValue($object, $value); } + + /** + * Get the class name of the given parameter's type, if possible. + * + * @see https://github.com/laravel/framework/blob/v6.18.25/src/Illuminate/Support/Reflector.php + */ + public static function getParameterClassName(ReflectionParameter $parameter): ?string + { + $type = $parameter->getType(); + + if (!$type instanceof ReflectionNamedType || $type->isBuiltin()) { + return null; + } + + $name = $type->getName(); + + if (($class = $parameter->getDeclaringClass()) instanceof ReflectionClass) { + if ($name === 'self') { + return $class->getName(); + } + + if ($name === 'parent' && ($parent = $class->getParentClass()) instanceof ReflectionClass) { + return $parent->getName(); + } + } + + return $name; + } } diff --git a/tests/.snapshots/success.txt b/tests/.snapshots/success.txt index ebc40173..8334095a 100644 --- a/tests/.snapshots/success.txt +++ b/tests/.snapshots/success.txt @@ -219,14 +219,6 @@ ✓ it creates unique test case names with ('Name 1', Pest\Plugin Object (), true) #3 ✓ it creates unique test case names - count - PASS Tests\Features\Depends - ✓ first - ✓ second - ✓ depends - ✓ depends with ...params - ✓ depends with defined arguments - ✓ depends run test only once - PASS Tests\Features\Exceptions ✓ it gives access the the underlying expectException ✓ it catch exceptions @@ -337,5 +329,13 @@ WARN Tests\Visual\Success - visual snapshot of test suite on success + PASS Tests\Features\Depends + ✓ first + ✓ second + ✓ depends + ✓ depends with ...params + ✓ depends with defined arguments + ✓ depends run test only once + Tests: 6 skipped, 198 passed Time: 5.46s