diff --git a/README.md b/README.md index cc858e8..5c0eee5 100644 --- a/README.md +++ b/README.md @@ -406,6 +406,17 @@ These methods were introduced in PHPUnit 10.0.0. [`Assert::assertStringEqualsStringIgnoringLineEndings()`]: https://docs.phpunit.de/en/main/assertions.html#assertstringequalsstringignoringlineendings [`Assert::assertStringContainsStringIgnoringLineEndings()`]: https://docs.phpunit.de/en/main/assertions.html#assertstringcontainsstringignoringlineendings +#### PHPUnit < 10.0.0: `Yoast\PHPUnitPolyfills\Polyfills\AssertIsList` + +Polyfills the following method: +| | +|---------------------------------| +| [`Assert::assertIsList()`] | + +This method was introduced in PHPUnit 10.0.0. + +[`Assert::assertIsList()`]: https://docs.phpunit.de/en/main/assertions.html#assertislist + ### Helper traits diff --git a/phpunitpolyfills-autoload.php b/phpunitpolyfills-autoload.php index 50d2268..37064f2 100644 --- a/phpunitpolyfills-autoload.php +++ b/phpunitpolyfills-autoload.php @@ -91,6 +91,10 @@ public static function load( $className ) { self::loadAssertObjectEquals(); return true; + case 'Yoast\PHPUnitPolyfills\Polyfills\AssertIsList': + self::loadAssertIsList(); + return true; + case 'Yoast\PHPUnitPolyfills\Polyfills\AssertIgnoringLineEndings': self::loadAssertIgnoringLineEndings(); return true; @@ -293,6 +297,23 @@ public static function loadAssertObjectEquals() { require_once __DIR__ . '/src/Polyfills/AssertObjectEquals_Empty.php'; } + /** + * Load the AssertIsList polyfill or an empty trait with the same name + * if a PHPUnit version is used which already contains this functionality. + * + * @return void + */ + public static function loadAssertIsList() { + if ( \method_exists( Assert::class, 'assertIsList' ) === false ) { + // PHPUnit < 10.0.0. + require_once __DIR__ . '/src/Polyfills/AssertIsList.php'; + return; + } + + // PHPUnit >= 10.0.0. + require_once __DIR__ . '/src/Polyfills/AssertIsList_Empty.php'; + } + /** * Load the AssertIgnoringLineEndings polyfill or an empty trait with the same name * if a PHPUnit version is used which already contains this functionality. diff --git a/src/Polyfills/AssertIsList.php b/src/Polyfills/AssertIsList.php new file mode 100644 index 0000000..a9b00f0 --- /dev/null +++ b/src/Polyfills/AssertIsList.php @@ -0,0 +1,100 @@ += 10.0.0 in which this polyfill is not needed. + */ +trait AssertIsList {} diff --git a/src/TestCases/TestCasePHPUnitGte8.php b/src/TestCases/TestCasePHPUnitGte8.php index 49abd0c..d3b290b 100644 --- a/src/TestCases/TestCasePHPUnitGte8.php +++ b/src/TestCases/TestCasePHPUnitGte8.php @@ -8,6 +8,7 @@ use Yoast\PHPUnitPolyfills\Polyfills\AssertFileEqualsSpecializations; use Yoast\PHPUnitPolyfills\Polyfills\AssertIgnoringLineEndings; use Yoast\PHPUnitPolyfills\Polyfills\AssertionRenames; +use Yoast\PHPUnitPolyfills\Polyfills\AssertIsList; use Yoast\PHPUnitPolyfills\Polyfills\AssertObjectEquals; use Yoast\PHPUnitPolyfills\Polyfills\EqualToSpecializations; use Yoast\PHPUnitPolyfills\Polyfills\ExpectExceptionMessageMatches; @@ -28,6 +29,7 @@ abstract class TestCase extends PHPUnit_TestCase { use AssertFileEqualsSpecializations; use AssertIgnoringLineEndings; use AssertionRenames; + use AssertIsList; use AssertObjectEquals; use EqualToSpecializations; use ExpectExceptionMessageMatches; diff --git a/src/TestCases/TestCasePHPUnitLte7.php b/src/TestCases/TestCasePHPUnitLte7.php index 314b336..2cc584d 100644 --- a/src/TestCases/TestCasePHPUnitLte7.php +++ b/src/TestCases/TestCasePHPUnitLte7.php @@ -9,6 +9,7 @@ use Yoast\PHPUnitPolyfills\Polyfills\AssertFileEqualsSpecializations; use Yoast\PHPUnitPolyfills\Polyfills\AssertIgnoringLineEndings; use Yoast\PHPUnitPolyfills\Polyfills\AssertionRenames; +use Yoast\PHPUnitPolyfills\Polyfills\AssertIsList; use Yoast\PHPUnitPolyfills\Polyfills\AssertIsType; use Yoast\PHPUnitPolyfills\Polyfills\AssertObjectEquals; use Yoast\PHPUnitPolyfills\Polyfills\AssertStringContains; @@ -33,6 +34,7 @@ abstract class TestCase extends PHPUnit_TestCase { use AssertFileEqualsSpecializations; use AssertIgnoringLineEndings; use AssertionRenames; + use AssertIsList; use AssertIsType; use AssertObjectEquals; use AssertStringContains; diff --git a/src/TestCases/XTestCase.php b/src/TestCases/XTestCase.php index 461862d..93b4ce9 100644 --- a/src/TestCases/XTestCase.php +++ b/src/TestCases/XTestCase.php @@ -9,6 +9,7 @@ use Yoast\PHPUnitPolyfills\Polyfills\AssertFileEqualsSpecializations; use Yoast\PHPUnitPolyfills\Polyfills\AssertIgnoringLineEndings; use Yoast\PHPUnitPolyfills\Polyfills\AssertionRenames; +use Yoast\PHPUnitPolyfills\Polyfills\AssertIsList; use Yoast\PHPUnitPolyfills\Polyfills\AssertIsType; use Yoast\PHPUnitPolyfills\Polyfills\AssertObjectEquals; use Yoast\PHPUnitPolyfills\Polyfills\AssertStringContains; @@ -35,6 +36,7 @@ abstract class XTestCase extends PHPUnit_TestCase { use AssertFileEqualsSpecializations; use AssertIgnoringLineEndings; use AssertionRenames; + use AssertIsList; use AssertIsType; use AssertObjectEquals; use AssertStringContains; diff --git a/tests/Polyfills/AssertIsListTest.php b/tests/Polyfills/AssertIsListTest.php new file mode 100644 index 0000000..6fbfff0 --- /dev/null +++ b/tests/Polyfills/AssertIsListTest.php @@ -0,0 +1,213 @@ +expectException( $this->getAssertionFailedExceptionName() ); + $this->expectExceptionMessageMatches( '`^Failed asserting that ' . $type . ' is a list`' ); + + $this->assertIsList( $actual ); + } + + /** + * Data provider. + * + * @return array + */ + public static function dataAssertIsListFailsOnInvalidInputType() { + // Only testing closed resource to not leak an open resource. + $resource = \fopen( __DIR__ . '/Fixtures/test.txt', 'r' ); + \fclose( $resource ); + + return [ + 'null' => [ + 'actual' => null, + 'type' => 'null', + ], + 'boolean' => [ + 'actual' => true, + 'type' => 'a boolean', + ], + 'integer' => [ + 'actual' => 10, + 'type' => 'an integer', + ], + 'float' => [ + 'actual' => 5.34, + 'type' => 'a float', + ], + 'string' => [ + 'actual' => 'text', + 'type' => 'a string', + ], + 'object' => [ + 'actual' => new stdClass(), + 'type' => 'an object', + ], + 'closed resource' => [ + 'actual' => $resource, + 'type' => ( \PHP_VERSION_ID > 70200 ) ? 'a closed resource' : 'a value of unknown type', + ], + ]; + } + + /** + * Verify availability and functionality of the assertIsList() method. + * + * @dataProvider dataAssertIsListPass + * + * @param array $actual The value to test. + * + * @return void + */ + public function testAssertIsListPass( $actual ) { + $this->assertIsList( $actual ); + } + + /** + * Data provider. + * + * @return array + */ + public static function dataAssertIsListPass() { + return [ + 'empty array' => [ [] ], + 'array without keys (integer values)' => [ [ 0, 1, 2 ] ], + 'array without keys (mixed values)' => [ [ 'string', 1.5, new stdClass(), [], null ] ], + 'array with consecutive numeric keys (ascending)' => [ + [ + 0 => 0, + 1 => 1, + 2 => 2, + ], + ], + 'array with partial keys, starting at 0' => [ + [ + 0 => 'apple', + 'orange', + ], + ], + ]; + } + + /** + * Verify that the assertIsList() method throws an error when the passed $array is not a list. + * + * @dataProvider dataAssertIsListFail + * + * @param array $actual The value to test. + * + * @return void + */ + public function testAssertIsListFail( $actual ) { + $this->expectException( $this->getAssertionFailedExceptionName() ); + $this->expectExceptionMessage( 'Failed asserting that an array is a list' ); + + static::assertIsList( $actual ); + } + + /** + * Data provider. + * + * @return array + */ + public static function dataAssertIsListFail() { + return [ + 'array with non-consecutive numeric keys' => [ + [ + 0 => 0, + 2 => 2, + 3 => 3, + ], + ], + 'array with consecutive numeric keys not starting at 0' => [ + [ + 3 => 0, + 4 => 1, + 5 => 2, + ], + ], + 'array with consecutive numeric keys (descending)' => [ + [ + 0 => 0, + -1 => 1, + -2 => 2, + ], + ], + 'array with string keys' => [ + [ + 'a' => 0, + 'b' => 1, + ], + ], + 'array with partial string keys' => [ + [ + 'a' => 'apple', + 'orange', + ], + ], + ]; + } + + /** + * Verify that the assertIsList() method fails a test with a custom failure message, + * when the custom $message parameter has been passed. + * + * @return void + */ + public function testAssertIsListFailsWithCustomMessage() { + $pattern = '`^This assertion failed for reason XYZ\s+Failed asserting that an array is a list\.`'; + + $this->expectException( $this->getAssertionFailedExceptionName() ); + $this->expectExceptionMessageMatches( $pattern ); + + $array = [ + 0 => 0, + 2 => 2, + ]; + + $this->assertIsList( $array, '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/TestCases/TestCaseTestTrait.php b/tests/TestCases/TestCaseTestTrait.php index aa07588..4778ea7 100644 --- a/tests/TestCases/TestCaseTestTrait.php +++ b/tests/TestCases/TestCaseTestTrait.php @@ -142,4 +142,13 @@ final public function testAvailabilityAssertObjectEquals() { final public function testAvailabilityAssertIgnoringLineEndings() { self::assertStringContainsStringIgnoringLineEndings( "b\nc", "a\r\nb\r\nc\r\nd" ); } + + /** + * Verify availability of trait polyfilled PHPUnit methods [16]. + * + * @return void + */ + final public function testAvailabilityAssertIsList() { + static::assertIsList( [ 0, 1, 2 ] ); + } }