diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 5fe8e1c..1edb2b0 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -20,7 +20,7 @@ jobs: strategy: matrix: - php: ['5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', 'nightly'] + php: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', 'nightly'] continue-on-error: ${{ matrix.php == 'nightly' }} @@ -62,10 +62,6 @@ jobs: # Bust the cache at least once a month - output format: YYYY-MM. custom-cache-suffix: $(date -u "+%Y-%m") - - name: "Lint PHP files against parse errors - PHP < 7.0" - if: ${{ matrix.php != 'nightly' && matrix.php < 7.0 }} - run: composer lint-lt70 -- --checkstyle | cs2pr - - name: "Lint PHP files against parse errors - PHP 7.0" if: ${{ matrix.php == '7.0' }} run: composer lint70 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e91ece7..3c754c0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,7 +20,7 @@ jobs: strategy: matrix: - php: ['5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3'] + php: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3'] phpunit: ['auto'] coverage: [true] experimental: [false] @@ -28,14 +28,10 @@ jobs: include: # Test against a version on the low-end of the PHPUnit versions supported for each PHP version. # Using the Composer `--prefer-lowest` option is, unfortunately, not viable, as - # it would result PHP 5.6 - 7.4 all using PHPUnit 5.7.21, which is not the intention. + # it would result PHP 7.0 - 7.4 all using PHPUnit 5.7.21, which is not the intention. # It also would run into trouble with PHP 8.5.12 being used on PHP 8.0+, while the # 8.5.12 release still contained a bug which makes it incompatible with PHP 8.1+, # even though it officially allows for it.. - - php: '5.6' - phpunit: '5.7.21' - coverage: true - experimental: false - php: '7.0' phpunit: '5.7.27' coverage: true @@ -211,9 +207,9 @@ jobs: # This should be sufficient to record the coverage for the PHAR specific code. # PHPUnit 5 is only supported for PHPUnit 5.7.21-latest. - - php: '5.6' + - php: '7.0' phpunit: '5.7.21' - - php: '5.6' + - php: '7.0' phpunit: '5' - php: '7.1' phpunit: '5.7.21' diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist index e11b822..0909cae 100644 --- a/.phpcs.xml.dist +++ b/.phpcs.xml.dist @@ -54,7 +54,6 @@ - @@ -68,10 +67,6 @@ polyfilled functionality from not being flagged in this repo. --> 5 - - - - @@ -179,22 +174,18 @@ /tests/Polyfills/Fixtures/*\.php$ - - - /tests/Polyfills/Fixtures/ChildValueObject\.php$ - /tests/Polyfills/Fixtures/ValueObject\.php$ + + /tests/Polyfills/Fixtures/ValueObjectParamNotRequired\.php$ - /tests/Polyfills/Fixtures/ValueObjectUnion\.php$ + + + /tests/Polyfills/Fixtures/ValueObjectNullableReturnType\.php$ /tests/Polyfills/Fixtures/ValueObjectUnion\.php$ - /tests/Polyfills/Fixtures/ValueObjectUnionNoReturnType\.php$ - - - /tests/Polyfills/Fixtures/ValueObjectParamNotRequired\.php$ - - /tests/Polyfills/Fixtures/ValueObject\.php$ + + /tests/Polyfills/Fixtures/ValueObjectUnionReturnType\.php$ diff --git a/README.md b/README.md index e312045..ef12396 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Set of polyfills for changed PHPUnit functionality to allow for creating PHPUnit Requirements ------------ -* PHP 5.6 or higher. +* PHP 7.0 or higher. * [PHPUnit] 5.7 - 10.x (automatically required via Composer). [PHPUnit]: https://packagist.org/packages/phpunit/phpunit @@ -391,17 +391,10 @@ This assertion expects an object to contain a comparator method in the object it The `assertObjectEquals()` assertion was introduced in PHPUnit 9.4.0. -> :information_source: Due to [limitations in how this assertion is implemented in PHPUnit] itself, it is currently not possible to create a single comparator method which will be compatible with both PHP < 7.0 and PHP 7.0 or higher. -> -> In effect two declarations of the same object would be needed to be compatible with PHP < 7.0 and PHP 7.0 and higher and still allow for testing the object using the `assertObjectEquals()` assertion. -> -> Due to this limitation, it is recommended to only use this assertion if the minimum supported PHP version of a project is PHP 7.0 or higher; or if the project does not run its tests on PHPUnit >= 9.4.0. -> -> The implementation of this assertion in the Polyfills is PHP cross-version compatible. - -[limitations in how this assertion is implemented in PHPUnit]: https://github.com/sebastianbergmann/phpunit/issues/4707 +> :information_source: In PHPUnit Polyfills 1.x and 2.x, the polyfill for this assertion had [some limitations][assertobjectequals-limitations]. These limitations are no longer present in PHPUnit Polyfills 3.x and the assertion now matches the PHPUnit native implementation. [`Assert::assertObjectEquals()`]: https://docs.phpunit.de/en/main/assertions.html#assertobjectequals +[assertobjectequals-limitations]: https://github.com/Yoast/PHPUnit-Polyfills/?tab=readme-ov-file#phpunit--940-yoastphpunitpolyfillspolyfillsassertobjectequals #### PHPUnit < 10.0.0: `Yoast\PHPUnitPolyfills\Polyfills\AssertIgnoringLineEndings` diff --git a/composer.json b/composer.json index 67e6a1b..4afa0cd 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,7 @@ "security": "https://github.com/Yoast/PHPUnit-Polyfills/security/policy" }, "require": { - "php": ">=5.6", + "php": ">=7.0", "phpunit/phpunit": "^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0" }, "require-dev": { @@ -59,22 +59,19 @@ }, "scripts": { "lint7": [ - "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php --show-deprecated --exclude vendor --exclude .git --exclude src/Exceptions/Error.php --exclude src/Exceptions/TypeError.php --exclude tests/Polyfills/Fixtures/ValueObjectUnion.php --exclude tests/Polyfills/Fixtures/ValueObjectUnionNoReturnType.php" + "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php --show-deprecated --exclude vendor --exclude .git --exclude tests/Polyfills/Fixtures/ValueObjectUnion.php --exclude tests/Polyfills/Fixtures/ValueObjectUnionReturnType.php" ], "lint70": [ - "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php --show-deprecated --exclude vendor --exclude .git --exclude src/Exceptions/Error.php --exclude src/Exceptions/TypeError.php --exclude tests/Polyfills/Fixtures/ValueObjectParamNotRequired.php --exclude tests/Polyfills/Fixtures/ValueObjectUnion.php --exclude tests/Polyfills/Fixtures/ValueObjectUnionNoReturnType.php" - ], - "lint-lt70": [ - "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php --show-deprecated --exclude vendor --exclude .git --exclude src/TestCases/TestCasePHPUnitGte8.php --exclude src/TestListeners/TestListenerDefaultImplementationPHPUnitGte7.php --exclude tests/Polyfills/Fixtures/ChildValueObject.php --exclude tests/Polyfills/Fixtures/ValueObject.php --exclude tests/Polyfills/Fixtures/ValueObjectParamNotRequired.php --exclude tests/Polyfills/Fixtures/ValueObjectUnion.php --exclude tests/Polyfills/Fixtures/ValueObjectUnionNoReturnType.php" + "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php --show-deprecated --exclude vendor --exclude .git --exclude src/Exceptions/Error.php --exclude src/Exceptions/TypeError.php --exclude tests/Polyfills/Fixtures/ValueObjectParamNotRequired.php --exclude tests/Polyfills/Fixtures/ValueObjectNullableReturnType.php --exclude tests/Polyfills/Fixtures/ValueObjectUnion.php --exclude tests/Polyfills/Fixtures/ValueObjectUnionReturnType.php" ], "lint-gte80": [ "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php --show-deprecated --exclude vendor --exclude .git" ], "lint-gte84": [ - "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php --show-deprecated --exclude vendor --exclude .git --exclude tests/Polyfills/Fixtures/ValueObjectNoReturnType.php" + "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php --show-deprecated --exclude vendor --exclude .git" ], "check-cs": [ - "@php ./vendor/squizlabs/php_codesniffer/bin/phpcs --runtime-set testVersion 5.6-" + "@php ./vendor/squizlabs/php_codesniffer/bin/phpcs --runtime-set testVersion 7.0-" ], "fix-cs": [ "@php ./vendor/squizlabs/php_codesniffer/bin/phpcbf" @@ -95,7 +92,6 @@ "scripts-descriptions": { "lint7": "Check the PHP files for parse errors. (PHP 7.1 - 7.4)", "lint70": "Check the PHP files for parse errors. (PHP 7.0)", - "lint-lt70": "Check the PHP files for parse errors. (PHP < 7.0)", "lint-gte80": "Check the PHP files for parse errors. (PHP 8.0 - 8.3)", "lint-gte84": "Check the PHP files for parse errors. (PHP 8.4+)", "check-cs": "Check the PHP files for code style violations and best practices.", diff --git a/phpunitpolyfills-autoload.php b/phpunitpolyfills-autoload.php index 4b1e743..363ef49 100644 --- a/phpunitpolyfills-autoload.php +++ b/phpunitpolyfills-autoload.php @@ -29,22 +29,6 @@ final class Autoload { * @return bool */ public static function load( $className ) { - /* - * Polyfill two PHP 7.0 classes. - * The autoloader will only be called for these if these classes don't already - * exist in PHP natively. - */ - if ( $className === 'Error' || $className === 'TypeError' ) { - $file = \realpath( __DIR__ . '/src/Exceptions/' . $className . '.php' ); - - if ( \is_string( $file ) && \file_exists( $file ) === true ) { - require_once $file; - return true; - } - - return false; - } - // Only load classes belonging to this library. if ( \stripos( $className, 'Yoast\PHPUnitPolyfills' ) !== 0 ) { return false; diff --git a/src/Exceptions/Error.php b/src/Exceptions/Error.php deleted file mode 100644 index 6a44e67..0000000 --- a/src/Exceptions/Error.php +++ /dev/null @@ -1,8 +0,0 @@ -$method($expected)` returns boolean true. + * - The method must have a declared bool return type. * * @param object $expected Expected value. * @param object $actual The value to test. @@ -103,14 +100,49 @@ final public static function assertObjectEquals( $expected, $actual, $method = ' $reflMethod = $reflObject->getMethod( $method ); /* - * As the next step, PHPUnit natively would validate the return type, - * but as return type declarations is a PHP 7.0+ feature, the polyfill - * skips this check in favour of checking the type of the actual - * returned value. - * - * Also see the upstream discussion about this: - * {@link https://github.com/sebastianbergmann/phpunit/issues/4707} + * Comparator method return type requirements validation. */ + $returnTypeError = \sprintf( + 'Comparison method %s::%s() does not declare bool return type.', + \get_class( $actual ), + $method + ); + + if ( $reflMethod->hasReturnType() === false ) { + throw new InvalidComparisonMethodException( $returnTypeError ); + } + + $returnType = $reflMethod->getReturnType(); + + if ( \class_exists( 'ReflectionNamedType' ) ) { + // PHP >= 7.1: guard against union/intersection return types. + if ( ( $returnType instanceof ReflectionNamedType ) === false ) { + throw new InvalidComparisonMethodException( $returnTypeError ); + } + } + elseif ( ( $returnType instanceof ReflectionType ) === false ) { + /* + * PHP 7.0. + * Checking for `ReflectionType` will not throw an error on union types, + * but then again union types are not supported on PHP 7.0. + */ + throw new InvalidComparisonMethodException( $returnTypeError ); + } + + if ( $returnType->allowsNull() === true ) { + throw new InvalidComparisonMethodException( $returnTypeError ); + } + + if ( \method_exists( $returnType, 'getName' ) ) { + // PHP 7.1+. + if ( $returnType->getName() !== 'bool' ) { + throw new InvalidComparisonMethodException( $returnTypeError ); + } + } + elseif ( (string) $returnType !== 'bool' ) { + // PHP 7.0. + throw new InvalidComparisonMethodException( $returnTypeError ); + } /* * Comparator method parameter requirements validation. @@ -142,55 +174,31 @@ final public static function assertObjectEquals( $expected, $actual, $method = ' $reflParameter = $reflMethod->getParameters()[0]; - if ( \method_exists( $reflParameter, 'hasType' ) ) { - // PHP >= 7.0. - $hasType = $reflParameter->hasType(); - if ( $hasType === false ) { + $hasType = $reflParameter->hasType(); + if ( $hasType === false ) { + throw new InvalidComparisonMethodException( $noDeclaredTypeError ); + } + + $type = $reflParameter->getType(); + if ( \class_exists( 'ReflectionNamedType' ) ) { + // PHP >= 7.1. + if ( ( $type instanceof ReflectionNamedType ) === false ) { throw new InvalidComparisonMethodException( $noDeclaredTypeError ); } - $type = $reflParameter->getType(); - if ( \class_exists( 'ReflectionNamedType' ) ) { - // PHP >= 7.1. - if ( ( $type instanceof ReflectionNamedType ) === false ) { - throw new InvalidComparisonMethodException( $noDeclaredTypeError ); - } - - $typeName = $type->getName(); - } - else { - /* - * PHP 7.0. - * Checking for `ReflectionType` will not throw an error on union types, - * but then again union types are not supported on PHP 7.0. - */ - if ( ( $type instanceof ReflectionType ) === false ) { - throw new InvalidComparisonMethodException( $noDeclaredTypeError ); - } - - $typeName = (string) $type; - } + $typeName = $type->getName(); } else { - // PHP < 7.0. - try { - /* - * Using `ReflectionParameter::getClass()` will trigger an autoload of the class, - * but that's okay as for a valid class type that would be triggered on the - * function call to the $method (at the end of this assertion) anyway. - */ - $hasType = $reflParameter->getClass(); - } catch ( ReflectionException $e ) { - // Class with a type declaration for a non-existent class. - throw new InvalidComparisonMethodException( $notAcceptableTypeError ); - } - - if ( ( $hasType instanceof ReflectionClass ) === false ) { - // Array or callable type. + /* + * PHP 7.0. + * Checking for `ReflectionType` will not throw an error on union types, + * but then again union types are not supported on PHP 7.0. + */ + if ( ( $type instanceof ReflectionType ) === false ) { throw new InvalidComparisonMethodException( $noDeclaredTypeError ); } - $typeName = $hasType->name; + $typeName = (string) $type; } /* @@ -209,16 +217,6 @@ final public static function assertObjectEquals( $expected, $actual, $method = ' */ $result = $actual->{$method}( $expected ); - if ( \is_bool( $result ) === false ) { - throw new InvalidComparisonMethodException( - \sprintf( - 'Comparison method %s::%s() does not return a boolean value.', - \get_class( $actual ), - $method - ) - ); - } - $msg = \sprintf( 'Failed asserting that two objects are equal. The objects are not equal according to %s::%s()', \get_class( $actual ), diff --git a/tests/Polyfills/AssertIgnoringLineEndingsTest.php b/tests/Polyfills/AssertIgnoringLineEndingsTest.php index 8f45b06..36945a8 100644 --- a/tests/Polyfills/AssertIgnoringLineEndingsTest.php +++ b/tests/Polyfills/AssertIgnoringLineEndingsTest.php @@ -47,7 +47,7 @@ public function testAssertStringEqualsStringIgnoringLineEndingsThrowsTypeErrorOn $msg = 'assertStringEqualsStringIgnoringLineEndings(): Argument #1 ($expected) must be of type string, '; } else { - // PHP 5/7. + // PHP 7. $msg = 'Argument 1 passed to assertStringEqualsStringIgnoringLineEndings() must be of type string, '; } @@ -75,7 +75,7 @@ public function testAssertStringEqualsStringIgnoringLineEndingsThrowsTypeErrorOn $msg = 'assertStringEqualsStringIgnoringLineEndings(): Argument #2 ($actual) must be of type string, '; } else { - // PHP 5/7. + // PHP 7. $msg = 'Argument 2 passed to assertStringEqualsStringIgnoringLineEndings() must be of type string, '; } @@ -221,7 +221,7 @@ public function testAssertStringContainsStringIgnoringLineEndingsThrowsTypeError $msg = 'assertStringContainsStringIgnoringLineEndings(): Argument #1 ($needle) must be of type string, '; } else { - // PHP 5/7. + // PHP 7. $msg = 'Argument 1 passed to assertStringContainsStringIgnoringLineEndings() must be of type string, '; } @@ -249,7 +249,7 @@ public function testAssertStringContainsStringIgnoringLineEndingsThrowsTypeError $msg = 'assertStringContainsStringIgnoringLineEndings(): Argument #2 ($haystack) must be of type string, '; } else { - // PHP 5/7. + // PHP 7. $msg = 'Argument 2 passed to assertStringContainsStringIgnoringLineEndings() must be of type string, '; } diff --git a/tests/Polyfills/AssertObjectEqualsPHPUnitLt940Test.php b/tests/Polyfills/AssertObjectEqualsPHPUnitLt940Test.php deleted file mode 100644 index 596def2..0000000 --- a/tests/Polyfills/AssertObjectEqualsPHPUnitLt940Test.php +++ /dev/null @@ -1,345 +0,0 @@ -=' ) ) { - $this->markTestSkipped( 'This test can not be run with the PHPUnit native implementation of assertObjectEquals()' ); - } - - if ( \version_compare( \PHP_VERSION_ID, '8.3.99', '>' ) ) { - $this->markTestSkipped( 'This test can not be run on PHP 8.4 or higher as PHPUnit < 9.4.0 is not compatible with PHP 8.4' ); - } - } - - /** - * Verify availability of the assertObjectEquals() method. - * - * @return void - */ - public function testAssertObjectEquals() { - $expected = new ValueObjectNoReturnType( 'test' ); - $actual = new ValueObjectNoReturnType( 'test' ); - $this->assertObjectEquals( $expected, $actual ); - } - - /** - * Verify behaviour when passing the $method parameter. - * - * @return void - */ - public function testAssertObjectEqualsCustomMethodName() { - $expected = new ValueObjectNoReturnType( 'different name' ); - $actual = new ValueObjectNoReturnType( 'different name' ); - $this->assertObjectEquals( $expected, $actual, 'nonDefaultName' ); - } - - /** - * Verify that the assertObjectEquals() method throws an error when the $expected parameter is not an object. - * - * @return void - */ - public function testAssertObjectEqualsFailsOnExpectedNotObject() { - $pattern = '`^Argument 1 passed to [^\s]*assertObjectEquals\(\) must be an object, string given`'; - - $this->expectException( TypeError::class ); - $this->expectExceptionMessageMatches( $pattern ); - - $actual = new ValueObjectNoReturnType( 'test' ); - $this->assertObjectEquals( 'className', $actual ); - } - - /** - * Verify that the assertObjectEquals() method throws an error when the $actual parameter is not an object. - * - * @return void - */ - public function testAssertObjectEqualsFailsOnActualNotObject() { - $pattern = '`^Argument 2 passed to [^\s]*assertObjectEquals\(\) must be an object, string given`'; - - $this->expectException( TypeError::class ); - $this->expectExceptionMessageMatches( $pattern ); - - $expected = new ValueObjectNoReturnType( 'test' ); - $this->assertObjectEquals( $expected, 'className' ); - } - - /** - * Verify that the assertObjectEquals() method throws an error when the $method parameter is not - * juggleable to a string. - * - * @return void - */ - public function testAssertObjectEqualsFailsOnMethodNotJuggleableToString() { - $pattern = '`^Argument 3 passed to [^\s]*assertObjectEquals\(\) must be of the type string, array given`'; - - $this->expectException( TypeError::class ); - $this->expectExceptionMessageMatches( $pattern ); - - $expected = new ValueObjectNoReturnType( 'test' ); - $actual = new ValueObjectNoReturnType( 'test' ); - $this->assertObjectEquals( $expected, $actual, [] ); - } - - /** - * Verify that the assertObjectEquals() method throws an error when the $actual object - * does not contain a method called $method. - * - * @return void - */ - public function testAssertObjectEqualsFailsOnMethodNotDeclared() { - $msg = 'Comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectNoReturnType::doesNotExist() does not exist.'; - - $this->expectException( self::COMPARATOR_EXCEPTION ); - $this->expectExceptionMessage( $msg ); - - $expected = new ValueObjectNoReturnType( 'test' ); - $actual = new ValueObjectNoReturnType( 'test' ); - $this->assertObjectEquals( $expected, $actual, 'doesNotExist' ); - } - - /** - * Verify that the assertObjectEquals() method throws an error when the $method accepts more than one parameter. - * - * @return void - */ - public function testAssertObjectEqualsFailsOnMethodAllowsForMoreParams() { - $msg = 'Comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectNoReturnType::equalsTwoParams() does not declare exactly one parameter.'; - - $this->expectException( self::COMPARATOR_EXCEPTION ); - $this->expectExceptionMessage( $msg ); - - $expected = new ValueObjectNoReturnType( 'test' ); - $actual = new ValueObjectNoReturnType( 'test' ); - $this->assertObjectEquals( $expected, $actual, 'equalsTwoParams' ); - } - - /** - * Verify that the assertObjectEquals() method throws an error when the $method is not a required parameter. - * - * @return void - */ - public function testAssertObjectEqualsFailsOnMethodParamNotRequired() { - $msg = 'Comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectNoReturnType::equalsParamNotRequired() does not declare exactly one parameter.'; - - $this->expectException( self::COMPARATOR_EXCEPTION ); - $this->expectExceptionMessage( $msg ); - - $expected = new ValueObjectNoReturnType( 'test' ); - $actual = new ValueObjectNoReturnType( 'test' ); - $this->assertObjectEquals( $expected, $actual, 'equalsParamNotRequired' ); - } - - /** - * Verify that the assertObjectEquals() method throws an error when the $method parameter - * does not have a type declaration. - * - * @return void - */ - public function testAssertObjectEqualsFailsOnMethodParamMissingTypeDeclaration() { - $msg = 'Parameter of comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectNoReturnType::equalsParamNoType() does not have a declared type.'; - - $this->expectException( self::COMPARATOR_EXCEPTION ); - $this->expectExceptionMessage( $msg ); - - $expected = new ValueObjectNoReturnType( 'test' ); - $actual = new ValueObjectNoReturnType( 'test' ); - $this->assertObjectEquals( $expected, $actual, 'equalsParamNoType' ); - } - - /** - * Verify that the assertObjectEquals() method throws an error when the $method parameter - * has a PHP 8.0+ union type declaration. - * - * @requires PHP 8.0 - * - * @return void - */ - #[RequiresPhp( '8.0' )] - public function testAssertObjectEqualsFailsOnMethodParamHasUnionTypeDeclaration() { - $msg = 'Parameter of comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectUnionNoReturnType::equalsParamUnionType() does not have a declared type.'; - - $this->expectException( self::COMPARATOR_EXCEPTION ); - $this->expectExceptionMessage( $msg ); - - $expected = new ValueObjectUnionNoReturnType( 'test' ); - $actual = new ValueObjectUnionNoReturnType( 'test' ); - $this->assertObjectEquals( $expected, $actual, 'equalsParamUnionType' ); - } - - /** - * Verify that the assertObjectEquals() method throws an error when the $method parameter - * does not have a class-based type declaration. - * - * @return void - */ - public function testAssertObjectEqualsFailsOnMethodParamNonClassTypeDeclaration() { - $msg = 'is not an accepted argument type for comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectNoReturnType::equalsParamNonClassType().'; - if ( \PHP_VERSION_ID < 70000 ) { - $msg = 'Parameter of comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectNoReturnType::equalsParamNonClassType() does not have a declared type.'; - } - - $this->expectException( self::COMPARATOR_EXCEPTION ); - $this->expectExceptionMessage( $msg ); - - $expected = new ValueObjectNoReturnType( 'test' ); - $actual = new ValueObjectNoReturnType( 'test' ); - $this->assertObjectEquals( $expected, $actual, 'equalsParamNonClassType' ); - } - - /** - * Verify that the assertObjectEquals() method throws an error when the $method parameter - * has a class-based type declaration, but for a class which doesn't exist. - * - * @return void - */ - public function testAssertObjectEqualsFailsOnMethodParamNonExistentClassTypeDeclaration() { - $msg = 'is not an accepted argument type for comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectNoReturnType::equalsParamNonExistentClassType().'; - - $this->expectException( self::COMPARATOR_EXCEPTION ); - $this->expectExceptionMessage( $msg ); - - $expected = new ValueObjectNoReturnType( 'test' ); - $actual = new ValueObjectNoReturnType( 'test' ); - $this->assertObjectEquals( $expected, $actual, 'equalsParamNonExistentClassType' ); - } - - /** - * Verify that the assertObjectEquals() method throws an error when $expected is not - * an instance of the type declared for the $method parameter. - * - * @return void - */ - public function testAssertObjectEqualsFailsOnMethodParamTypeMismatch() { - $msg = 'is not an accepted argument type for comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectNoReturnType::equals().'; - - $this->expectException( self::COMPARATOR_EXCEPTION ); - $this->expectExceptionMessage( $msg ); - - $actual = new ValueObjectNoReturnType( 'test' ); - $this->assertObjectEquals( new stdClass(), $actual ); - } - - /** - * Verify that the assertObjectEquals() method throws an error when the declared return type/ - * the return value is not boolean. - * - * @return void - */ - public function testAssertObjectEqualsFailsOnNonBooleanReturnValue() { - $msg = 'Comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectNoReturnType::equalsNonBooleanReturnType() does not return a boolean value.'; - - $this->expectException( self::COMPARATOR_EXCEPTION ); - $this->expectExceptionMessage( $msg ); - - $expected = new ValueObjectNoReturnType( 100 ); - $actual = new ValueObjectNoReturnType( 100 ); - $this->assertObjectEquals( $expected, $actual, 'equalsNonBooleanReturnType' ); - } - - /** - * Verify that the assertObjectEquals() method fails a test when a call to method - * determines that the objects are not equal. - * - * @return void - */ - public function testAssertObjectEqualsFailsAsNotEqual() { - $msg = 'Failed asserting that two objects are equal.'; - - $this->expectException( $this->getAssertionFailedExceptionName() ); - $this->expectExceptionMessage( $msg ); - - $expected = new ValueObjectNoReturnType( 'test' ); - $actual = new ValueObjectNoReturnType( 'testing... 1..2..3' ); - $this->assertObjectEquals( $expected, $actual ); - } - - /** - * Verify that the assertObjectEquals() method fails a test with a custom failure message, when a call - * to the method determines that the objects are not equal and the custom $message parameter has been passed. - * - * @return void - */ - public function testAssertObjectEqualsFailsAsNotEqualWithCustomMessage() { - $pattern = '`^This assertion failed for reason XYZ\s+Failed asserting that two objects are equal\.`'; - - $this->expectException( $this->getAssertionFailedExceptionName() ); - $this->expectExceptionMessageMatches( $pattern ); - - $expected = new ValueObjectNoReturnType( 'test' ); - $actual = new ValueObjectNoReturnType( 'testing... 1..2..3' ); - $this->assertObjectEquals( $expected, $actual, 'equals', 'This assertion failed for reason XYZ' ); - } - - /** - * Helper function: retrieve the name of the "assertion failed" exception to expect (PHPUnit cross-version). - * - * @return string - */ - public function getAssertionFailedExceptionName() { - $exception = AssertionFailedError::class; - if ( \class_exists( PHPUnit_Framework_AssertionFailedError::class ) ) { - // PHPUnit < 6. - $exception = PHPUnit_Framework_AssertionFailedError::class; - } - - return $exception; - } -} diff --git a/tests/Polyfills/AssertObjectEqualsTest.php b/tests/Polyfills/AssertObjectEqualsTest.php index ca754ff..3d78a90 100644 --- a/tests/Polyfills/AssertObjectEqualsTest.php +++ b/tests/Polyfills/AssertObjectEqualsTest.php @@ -20,23 +20,17 @@ use Yoast\PHPUnitPolyfills\Polyfills\ExpectExceptionMessageMatches; use Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ChildValueObject; use Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObject; +use Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectNullableReturnType; use Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectParamNotRequired; use Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectUnion; +use Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectUnionReturnType; /** * Availability test for the function polyfilled by the AssertObjectEquals trait. * - * Due to the use of return types in the classes under test (fixtures), these - * tests can only run on PHP 7.0 and higher. - * - * The `AssertObjectEqualsPHPUnitLt940Test` class mirrors this test class. - * * @covers \Yoast\PHPUnitPolyfills\Polyfills\AssertObjectEquals - * - * @requires PHP 7.0 */ #[CoversClass( AssertObjectEquals::class )] -#[RequiresPhp( '7.0' )] final class AssertObjectEqualsTest extends TestCase { use AssertObjectEquals; @@ -109,7 +103,7 @@ public function testAssertObjectEqualsFailsOnExpectedNotObject() { $this->expectExceptionMessage( $msg ); } else { - // PHP 5/7 or PHP 8 with the polyfill. + // PHP 7 or PHP 8 with the polyfill. $pattern = '`^Argument 1 passed to [^\s]*assertObjectEquals\(\) must be an object, string given`'; $this->expectExceptionMessageMatches( $pattern ); } @@ -133,7 +127,7 @@ public function testAssertObjectEqualsFailsOnActualNotObject() { $this->expectExceptionMessage( $msg ); } else { - // PHP 5/7. + // PHP 7. $pattern = '`^Argument 2 passed to [^\s]*assertObjectEquals\(\) must be an object, string given`'; $this->expectExceptionMessageMatches( $pattern ); } @@ -158,7 +152,7 @@ public function testAssertObjectEqualsFailsOnMethodNotJuggleableToString() { $this->expectExceptionMessage( $msg ); } else { - // PHP 5/7. + // PHP 7. $pattern = '`^Argument 3 passed to [^\s]*assertObjectEquals\(\) must be of the type string, array given`'; $this->expectExceptionMessageMatches( $pattern ); } @@ -191,6 +185,100 @@ public function testAssertObjectEqualsFailsOnMethodNotDeclared() { $this->assertObjectEquals( $expected, $actual, 'doesNotExist' ); } + /** + * Verify that the assertObjectEquals() method throws an error when no return type is declared. + * + * @return void + */ + public function testAssertObjectEqualsFailsOnMissingReturnType() { + $msg = 'Comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObject::equalsMissingReturnType() does not declare bool return type.'; + + $exception = self::COMPARATOR_EXCEPTION; + if ( \class_exists( ComparisonMethodDoesNotDeclareBoolReturnTypeException::class ) ) { + // PHPUnit > 9.4.0. + $exception = ComparisonMethodDoesNotDeclareBoolReturnTypeException::class; + } + + $this->expectException( $exception ); + $this->expectExceptionMessage( $msg ); + + $expected = new ValueObject( 100 ); + $actual = new ValueObject( 100 ); + $this->assertObjectEquals( $expected, $actual, 'equalsMissingReturnType' ); + } + + /** + * Verify that the assertObjectEquals() method throws an error when the declared return type in a union, intersection or DNF type. + * + * @requires PHP 8.0 + * + * @return void + */ + #[RequiresPhp( '8.0' )] + public function testAssertObjectEqualsFailsOnNonNamedTypeReturnType() { + $msg = 'Comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectUnionReturnType::equalsUnionReturnType() does not declare bool return type.'; + + $exception = self::COMPARATOR_EXCEPTION; + if ( \class_exists( ComparisonMethodDoesNotDeclareBoolReturnTypeException::class ) ) { + // PHPUnit > 9.4.0. + $exception = ComparisonMethodDoesNotDeclareBoolReturnTypeException::class; + } + + $this->expectException( $exception ); + $this->expectExceptionMessage( $msg ); + + $expected = new ValueObjectUnionReturnType( 100 ); + $actual = new ValueObjectUnionReturnType( 100 ); + $this->assertObjectEquals( $expected, $actual, 'equalsUnionReturnType' ); + } + + /** + * Verify that the assertObjectEquals() method throws an error when the declared return type is nullable. + * + * @requires PHP 7.1 + * + * @return void + */ + #[RequiresPhp( '7.1' )] + public function testAssertObjectEqualsFailsOnNullableReturnType() { + $msg = 'Comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObjectNullableReturnType::equalsNullableReturnType() does not declare bool return type.'; + + $exception = self::COMPARATOR_EXCEPTION; + if ( \class_exists( ComparisonMethodDoesNotDeclareBoolReturnTypeException::class ) ) { + // PHPUnit > 9.4.0. + $exception = ComparisonMethodDoesNotDeclareBoolReturnTypeException::class; + } + + $this->expectException( $exception ); + $this->expectExceptionMessage( $msg ); + + $expected = new ValueObjectNullableReturnType( 100 ); + $actual = new ValueObjectNullableReturnType( 100 ); + $this->assertObjectEquals( $expected, $actual, 'equalsNullableReturnType' ); + } + + /** + * Verify that the assertObjectEquals() method throws an error when the declared return type is not boolean. + * + * @return void + */ + public function testAssertObjectEqualsFailsOnNonBooleanReturnType() { + $msg = 'Comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObject::equalsNonBooleanReturnType() does not declare bool return type.'; + + $exception = self::COMPARATOR_EXCEPTION; + if ( \class_exists( ComparisonMethodDoesNotDeclareBoolReturnTypeException::class ) ) { + // PHPUnit > 9.4.0. + $exception = ComparisonMethodDoesNotDeclareBoolReturnTypeException::class; + } + + $this->expectException( $exception ); + $this->expectExceptionMessage( $msg ); + + $expected = new ValueObject( 100 ); + $actual = new ValueObject( 100 ); + $this->assertObjectEquals( $expected, $actual, 'equalsNonBooleanReturnType' ); + } + /** * Verify that the assertObjectEquals() method throws an error when the $method accepts more than one parameter. * @@ -355,30 +443,6 @@ public function testAssertObjectEqualsFailsOnMethodParamTypeMismatch() { $this->assertObjectEquals( new stdClass(), $actual ); } - /** - * Verify that the assertObjectEquals() method throws an error when the declared return type/ - * the return value is not boolean. - * - * @return void - */ - public function testAssertObjectEqualsFailsOnNonBooleanReturnValue() { - $msg = 'Comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObject::equalsNonBooleanReturnType() does not return a boolean value.'; - - $exception = self::COMPARATOR_EXCEPTION; - if ( \class_exists( ComparisonMethodDoesNotDeclareBoolReturnTypeException::class ) ) { - // PHPUnit > 9.4.0. - $msg = 'Comparison method Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObject::equalsNonBooleanReturnType() does not declare bool return type.'; - $exception = ComparisonMethodDoesNotDeclareBoolReturnTypeException::class; - } - - $this->expectException( $exception ); - $this->expectExceptionMessage( $msg ); - - $expected = new ValueObject( 100 ); - $actual = new ValueObject( 100 ); - $this->assertObjectEquals( $expected, $actual, 'equalsNonBooleanReturnType' ); - } - /** * Verify that the assertObjectEquals() method fails a test when a call to method * determines that the objects are not equal. diff --git a/tests/Polyfills/AssertObjectPropertyTest.php b/tests/Polyfills/AssertObjectPropertyTest.php index 8ccc2f0..16c31b6 100644 --- a/tests/Polyfills/AssertObjectPropertyTest.php +++ b/tests/Polyfills/AssertObjectPropertyTest.php @@ -61,7 +61,7 @@ public function testAssertObjectHasPropertyFailsOnInvalidInputTypePropertyName( $this->expectExceptionMessage( $msg ); } else { - // PHP 5/7. + // PHP 7. $pattern = '`^Argument 1 passed to [^\s]*assertObjectHasProperty\(\) must be of (the )?type string, `'; $this->expectExceptionMessageMatches( $pattern ); } @@ -91,7 +91,7 @@ public function testAssertObjectNotHasPropertyFailsOnInvalidInputTypePropertyNam $this->expectExceptionMessage( $msg ); } else { - // PHP 5/7. + // PHP 7. $pattern = '`^Argument 1 passed to [^\s]*assertObjectNotHasProperty\(\) must be of (the )?type string, `'; $this->expectExceptionMessageMatches( $pattern ); } @@ -138,7 +138,7 @@ public function testAssertObjectHasPropertyFailsOnInvalidInputTypeObject( $input $this->expectExceptionMessage( $msg ); } else { - // PHP 5/7. + // PHP 7. $pattern = '`^Argument 2 passed to [^\s]*assertObjectHasProperty\(\) must be (of type|an) object, `'; $this->expectExceptionMessageMatches( $pattern ); } @@ -164,7 +164,7 @@ public function testAssertObjectNotHasPropertyFailsOnInvalidInputTypeObject( $in $this->expectExceptionMessage( $msg ); } else { - // PHP 5/7. + // PHP 7. $pattern = '`^Argument 2 passed to [^\s]*assertObjectNotHasProperty\(\) must be (of type|an) object, `'; $this->expectExceptionMessageMatches( $pattern ); } diff --git a/tests/Polyfills/Fixtures/ValueObject.php b/tests/Polyfills/Fixtures/ValueObject.php index 777a984..7d3a013 100644 --- a/tests/Polyfills/Fixtures/ValueObject.php +++ b/tests/Polyfills/Fixtures/ValueObject.php @@ -47,6 +47,28 @@ public function nonDefaultName( ValueObject $other ): bool { return ( $this->value === $other->value ); } + /** + * Comparator method: incorrectly declared - missing return type. + * + * @param self $other Object to compare. + * + * @return bool + */ + public function equalsMissingReturnType( self $other ) { + return ( $this->value === $other->value ); + } + + /** + * Comparator method: incorrectly declared - non-boolean return type/value. + * + * @param self $other Object to compare. + * + * @return bool + */ + public function equalsNonBooleanReturnType( self $other ): int { + return ( $this->value <=> $other->value ); + } + /** * Comparator method: incorrectly declared - more than one parameter. * @@ -91,15 +113,4 @@ public function equalsParamNonClassType( array $other ): bool { public function equalsParamNonExistentClassType( ClassWhichDoesntExist $other ): bool { return ( $this->value === $other->value ); } - - /** - * Comparator method: incorrectly declared - non-boolean return type/value. - * - * @param self $other Object to compare. - * - * @return bool - */ - public function equalsNonBooleanReturnType( self $other ): int { - return ( $this->value <=> $other->value ); - } } diff --git a/tests/Polyfills/Fixtures/ValueObjectNoReturnType.php b/tests/Polyfills/Fixtures/ValueObjectNoReturnType.php deleted file mode 100644 index 913611d..0000000 --- a/tests/Polyfills/Fixtures/ValueObjectNoReturnType.php +++ /dev/null @@ -1,124 +0,0 @@ -value = $value; - } - - /** - * Comparator method: correctly declared. - * - * @param ValueObjectNoReturnType $other Object to compare. - * - * @return bool - */ - public function equals( ValueObjectNoReturnType $other ) { - return ( $this->value === $other->value ); - } - - /** - * Comparator method: correctly declared and with self as type instead of the class name. - * - * @param self $other Object to compare. - * - * @return bool - */ - public function nonDefaultName( self $other ) { - return ( $this->value === $other->value ); - } - - /** - * Comparator method: incorrectly declared - more than one parameter. - * - * @param ValueObjectNoReturnType $other Object to compare. - * @param mixed $param Just testing. - * - * @return bool - */ - public function equalsTwoParams( $other, $param ) { - return ( $param && $this->value === $other->value ); - } - - /** - * Comparator method: incorrectly declared - parameter is not required. - * - * @param self|null $other Object to compare. - * - * @return bool - */ - public function equalsParamNotRequired( self $other = null ) { - return ( $this->value === $other->value ); - } - - /** - * Comparator method: incorrectly declared - parameter is not typed. - * - * @param ValueObjectNoReturnType $other Object to compare. - * - * @return bool - */ - public function equalsParamNoType( $other ) { - return ( $this->value === $other->value ); - } - - /** - * Comparator method: incorrectly declared - parameter has a non-classname type. - * - * @param array $other Object to compare. - * - * @return bool - */ - public function equalsParamNonClassType( array $other ) { - return ( $this->value === $other->value ); - } - - /** - * Comparator method: incorrectly declared - parameter has a non-existent classname type. - * - * @param ClassWhichDoesntExist $other Object to compare. - * - * @return bool - */ - public function equalsParamNonExistentClassType( ClassWhichDoesntExist $other ) { - return ( $this->value === $other->value ); - } - - /** - * Comparator method: incorrectly declared - non-boolean return type/value. - * - * @param self $other Object to compare. - * - * @return int - */ - public function equalsNonBooleanReturnType( self $other ) { - if ( $this->value === $other->value ) { - return 0; - } - - if ( $this->value > $other->value ) { - return 1; - } - - return -1; - } -} diff --git a/tests/Polyfills/Fixtures/ValueObjectNullableReturnType.php b/tests/Polyfills/Fixtures/ValueObjectNullableReturnType.php new file mode 100644 index 0000000..45c98af --- /dev/null +++ b/tests/Polyfills/Fixtures/ValueObjectNullableReturnType.php @@ -0,0 +1,39 @@ +value = $value; + } + + /** + * Comparator method: incorrectly declared - return type is nullable. + * + * @param self $other Object to compare. + * + * @return bool + */ + public function equalsNullableReturnType( self $other ): ?bool { + return ( $this->value === $other->value ); + } +} diff --git a/tests/Polyfills/Fixtures/ValueObjectUnionNoReturnType.php b/tests/Polyfills/Fixtures/ValueObjectUnionReturnType.php similarity index 55% rename from tests/Polyfills/Fixtures/ValueObjectUnionNoReturnType.php rename to tests/Polyfills/Fixtures/ValueObjectUnionReturnType.php index efa2387..d937600 100644 --- a/tests/Polyfills/Fixtures/ValueObjectUnionNoReturnType.php +++ b/tests/Polyfills/Fixtures/ValueObjectUnionReturnType.php @@ -4,8 +4,11 @@ /** * Fixture to test the AssertObjectEquals trait. + * + * This `equals*()` method needs to be in this separate fixture as it needs + * a union type declaration (PHP 8.0+). */ -class ValueObjectUnionNoReturnType { +class ValueObjectUnionReturnType { /** * The value. @@ -24,13 +27,13 @@ public function __construct( $value ) { } /** - * Comparator method: incorrectly declared - parameter has a union type. + * Comparator method: incorrectly declared - union type as return type. * - * @param self|OtherClass|array $other Object to compare. + * @param self $other Object to compare. * * @return bool */ - public function equalsParamUnionType( self|OtherClass|array $other ) { + public function equalsUnionReturnType( self $other ): bool|int { return ( $this->value === $other->value ); } } diff --git a/tests/TestCases/TestCaseTestTrait.php b/tests/TestCases/TestCaseTestTrait.php index b41c8be..ae9a67b 100644 --- a/tests/TestCases/TestCaseTestTrait.php +++ b/tests/TestCases/TestCaseTestTrait.php @@ -3,7 +3,6 @@ namespace Yoast\PHPUnitPolyfills\Tests\TestCases; use Exception; -use PHPUnit\Framework\Attributes\RequiresPhp; use stdClass; use Yoast\PHPUnitPolyfills\Tests\Polyfills\AssertFileEqualsSpecializationsTest; use Yoast\PHPUnitPolyfills\Tests\Polyfills\Fixtures\ValueObject; @@ -126,11 +125,8 @@ final public function testAvailabilityEqualToSpecializations() { /** * Verify availability of trait polyfilled PHPUnit methods [14]. * - * @requires PHP 7.0 - * * @return void */ - #[RequiresPhp( '7.0' )] final public function testAvailabilityAssertObjectEquals() { $expected = new ValueObject( 'test' ); $actual = new ValueObject( 'test' );