diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index 75fec45..9e59ebe 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -26,7 +26,9 @@ $license->save(); -$config = PhpCsFixer\Config\Factory::fromRuleSet(new PhpCsFixer\Config\RuleSet\Php73($license->header())); +$config = PhpCsFixer\Config\Factory::fromRuleSet(new PhpCsFixer\Config\RuleSet\Php73($license->header()), [ + 'strict_comparison' => false, +]); $config->getFinder() ->exclude([ diff --git a/CHANGELOG.md b/CHANGELOG.md index 958b6fd..1c6c0dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## Unreleased -For a full diff see [`1902cc2...main`][1902cc2...main]. +For a full diff see [`a5f2657...main`][a5f2657...main]. -[1902cc2...main]: https://github.com/ergebnis/data-provider/compare/1902cc2...main +### Added + +* Imported data providers from [`ergebnis/test-util`](https://github.com/ergebnis/test-util) ([#1]), by [@localheinz] + +[a5f2657...main]: https://github.com/ergebnis/data-provider/compare/a5f2657...main + +[#1]: https://github.com/ergebnis/data-provider/pull/1 + +[@localheinz]: https://github.com/localheinz diff --git a/README.md b/README.md index 6d0d225..08e97c9 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,103 @@ composer require --dev ergebnis/data-provider ## Usage -:bulb: This is a great place for showing a few usage examples! +This package provides the following generic data providers: +* [`Ergebnis\DataProvider\BoolProvider`](https://github.com/ergebnis/data-provider#dataproviderboolprovider) +* [`Ergebnis\DataProvider\FloatProvider`](https://github.com/ergebnis/data-provider#dataproviderfloatprovider) +* [`Ergebnis\DataProvider\IntProvider`](https://github.com/ergebnis/data-provider#dataproviderintprovider) +* [`Ergebnis\DataProvider\NullProvider`](https://github.com/ergebnis/data-provider#dataprovidernullprovider) +* [`Ergebnis\DataProvider\ObjectProvider`](https://github.com/ergebnis/data-provider#dataproviderobjectprovider) +* [`Ergebnis\DataProvider\ResourceProvider`](https://github.com/ergebnis/data-provider#dataproviderresourceprovider) +* [`Ergebnis\DataProvider\StringProvider`](https://github.com/ergebnis/data-provider#dataproviderstringprovider) + +Since it is possible to use multiple `@dataProvider` annotations for test methods, these generic data providers allow for reuse and composition of data providers: + +```php +expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Value can not be an empty or blank string.'); + + UserName::fromString($value); + } +} +``` + +#### `DataProvider\BoolProvider` + +* `arbitrary()` provides `true`, `false` +* `false()` provides `false` +* `true()` provides `true` + +For examples, see [`Ergebnis\DataProvider\Test\Unit\BoolProviderTest`](test/Unit/BoolProviderTest.php). + +#### `DataProvider\FloatProvider` + +* `arbitrary()` provides arbitrary `float`s +* `greaterThanOne()` provides `int`s greater than `1.0` +* `greaterThanZero()` provides `int`s greater than `0.0` +* `lessThanOne()` provides `int`s less than `1.0` +* `lessThanZero()` provides `int`s less than `0.0` +* `one()` provides `1.0` +* `zero()` provides `0.0` + +For examples, see [`Ergebnis\DataProvider\Test\Unit\FloatProviderTest`](test/Unit/FloatProviderTest.php). + +#### `DataProvider\IntProvider` + +* `arbitrary()` provides arbitrary `int`s +* `greaterThanOne()` provides `int`s greater than `1` +* `greaterThanZero()` provides `int`s greater than `0` +* `lessThanOne()` provides `int`s less than `1` +* `lessThanZero()` provides `int`s less than `0` +* `one()` provides `1` +* `zero()` provides `0` + +For examples, see [`Ergebnis\DataProvider\Test\Unit\IntProviderTest`](test/Unit/IntProviderTest.php). + +#### `DataProvider\NullProvider` + +* `null()` provides `null` + +For examples, see [`Ergebnis\DataProvider\Test\Unit\NullProviderTest`](test/Unit/NullProviderTest.php). + +#### `DataProvider\ObjectProvider` + +* `object()` provides an instance of `stdClass` + +For examples, see [`Ergebnis\DataProvider\Test\Unit\ObjectProviderTest`](test/Unit/ObjectProviderTest.php). + +#### `DataProvider\ResourceProvider` + +* `resource()` provides a `resource` + +For examples, see [`Ergebnis\DataProvider\Test\Unit\ResourceProviderTest`](test/Unit/ResourceProviderTest.php). + +#### `DataProvider\StringProvider` + +* `arbitrary()` provides arbitrary `string`s +* `blank()` provides `string`s consisting of whitespace characters only +* `empty()` provides an empty `string` +* `trimmed()` provides non-empty, non-blank `strings` without leading and trailing whitespace +* `untrimmed()` provides non-empty, non-blank `string`s with additional leading and trailing whitespace +* `withWhitespace()` provides non-empty, non-blank, trimmed `string`s containing whitespace + +For examples, see [`Ergebnis\DataProvider\Test\Unit\StringProviderTest`](test/Unit/StringProviderTest.php). ## Changelog Please have a look at [`CHANGELOG.md`](CHANGELOG.md). diff --git a/composer.json b/composer.json index f550738..6f9369f 100644 --- a/composer.json +++ b/composer.json @@ -15,13 +15,13 @@ } ], "require": { - "php": "^7.3 || ^8.0" + "php": "^7.3 || ^8.0", + "fakerphp/faker": "^1.16.0" }, "require-dev": { "ergebnis/composer-normalize": "^2.16.0", "ergebnis/license": "^1.1.0", "ergebnis/php-cs-fixer-config": "^3.2.1", - "ergebnis/test-util": "^1.5.0", "phpunit/phpunit": "~9.5.10", "psalm/plugin-phpunit": "~0.16.1", "vimeo/psalm": "^4.13.1" diff --git a/composer.lock b/composer.lock index 0de3c41..f2f48c6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,189 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "af5dd83edafa2c6d7222301a5e814ee9", - "packages": [], + "content-hash": "c95ce85293d4bbfd3f80094d7170a46a", + "packages": [ + { + "name": "fakerphp/faker", + "version": "v1.16.0", + "source": { + "type": "git", + "url": "https://github.com/FakerPHP/Faker.git", + "reference": "271d384d216e5e5c468a6b28feedf95d49f83b35" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/271d384d216e5e5c468a6b28feedf95d49f83b35", + "reference": "271d384d216e5e5c468a6b28feedf95d49f83b35", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^2.2" + }, + "conflict": { + "fzaninotto/faker": "*" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "ext-intl": "*", + "symfony/phpunit-bridge": "^4.4 || ^5.2" + }, + "suggest": { + "ext-curl": "Required by Faker\\Provider\\Image to download images.", + "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", + "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", + "ext-mbstring": "Required for multibyte Unicode string functionality." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "v1.16-dev" + } + }, + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "support": { + "issues": "https://github.com/FakerPHP/Faker/issues", + "source": "https://github.com/FakerPHP/Faker/tree/v1.16.0" + }, + "time": "2021-09-06T14:53:37+00:00" + }, + { + "name": "psr/container", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.1" + }, + "time": "2021-03-05T17:36:06+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v2.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/6f981ee24cf69ee7ce9736146d1c57c2780598a8", + "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-07-12T14:48:14+00:00" + } + ], "packages-dev": [ { "name": "amphp/amp", @@ -649,83 +830,6 @@ ], "time": "2020-05-25T17:44:05+00:00" }, - { - "name": "ergebnis/classy", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/ergebnis/classy.git", - "reference": "72840bda3ce8b7bdc9362e8646141eb3c5ca9947" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ergebnis/classy/zipball/72840bda3ce8b7bdc9362e8646141eb3c5ca9947", - "reference": "72840bda3ce8b7bdc9362e8646141eb3c5ca9947", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "ergebnis/composer-normalize": "^2.13.2", - "ergebnis/license": "^1.1.0", - "ergebnis/php-cs-fixer-config": "^2.13.0", - "ergebnis/phpstan-rules": "~0.15.3", - "ergebnis/test-util": "^1.0.0", - "infection/infection": "~0.15.3", - "phpstan/extension-installer": "^1.1.0", - "phpstan/phpstan": "~0.12.70", - "phpstan/phpstan-deprecation-rules": "~0.12.6", - "phpstan/phpstan-strict-rules": "~0.12.9", - "phpunit/phpunit": "^8.5.14", - "psalm/plugin-phpunit": "~0.15.0", - "vimeo/psalm": "^4.4.1", - "zendframework/zend-file": "^2.8.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.0-dev" - } - }, - "autoload": { - "psr-4": { - "Ergebnis\\Classy\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Andreas Möller", - "email": "am@localheinz.com" - } - ], - "description": "Provides a finder for classy constructs (classes, interfaces, and traits).", - "homepage": "https://github.com/ergebnis/classy", - "keywords": [ - "classes", - "classy", - "constructs", - "finder", - "interfaces", - "traits" - ], - "support": { - "issues": "https://github.com/ergebnis/classy/issues", - "source": "https://github.com/ergebnis/classy" - }, - "funding": [ - { - "url": "https://github.com/localheinz", - "type": "github" - } - ], - "time": "2021-02-01T08:25:30+00:00" - }, { "name": "ergebnis/composer-normalize", "version": "2.16.0", @@ -1072,147 +1176,6 @@ ], "time": "2021-11-15T21:44:59+00:00" }, - { - "name": "ergebnis/test-util", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/ergebnis/test-util.git", - "reference": "7c85925bca8b2d2985eb7a208f53114dc64c780b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ergebnis/test-util/zipball/7c85925bca8b2d2985eb7a208f53114dc64c780b", - "reference": "7c85925bca8b2d2985eb7a208f53114dc64c780b", - "shasum": "" - }, - "require": { - "ergebnis/classy": "^1.1.1", - "fakerphp/faker": "^1.14.1", - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "ergebnis/composer-normalize": "^2.13.3", - "ergebnis/license": "^1.1.0", - "ergebnis/php-cs-fixer-config": "^2.13.0", - "ergebnis/phpstan-rules": "~0.15.3", - "infection/infection": "~0.15.3", - "phpstan/extension-installer": "^1.1.0", - "phpstan/phpstan": "~0.12.65", - "phpstan/phpstan-deprecation-rules": "~0.12.6", - "phpstan/phpstan-phpunit": "~0.12.18", - "phpstan/phpstan-strict-rules": "~0.12.9", - "phpunit/phpunit": "^8.5.15", - "psalm/plugin-phpunit": "~0.15.1", - "vimeo/psalm": "^4.7.0" - }, - "type": "library", - "extra": { - "composer-normalize": { - "indent-size": 2, - "indent-style": "space" - } - }, - "autoload": { - "psr-4": { - "Ergebnis\\Test\\Util\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Andreas Möller", - "email": "am@localheinz.com" - } - ], - "description": "Provides a helper trait and generic data providers for tests.", - "homepage": "https://github.com/ergebnis/test-util", - "keywords": [ - "assertion", - "faker", - "phpunit", - "test" - ], - "support": { - "issues": "https://github.com/ergebnis/test-util/issues", - "source": "https://github.com/ergebnis/test-util" - }, - "funding": [ - { - "url": "https://github.com/localheinz", - "type": "github" - } - ], - "time": "2021-03-30T15:07:05+00:00" - }, - { - "name": "fakerphp/faker", - "version": "v1.16.0", - "source": { - "type": "git", - "url": "https://github.com/FakerPHP/Faker.git", - "reference": "271d384d216e5e5c468a6b28feedf95d49f83b35" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/271d384d216e5e5c468a6b28feedf95d49f83b35", - "reference": "271d384d216e5e5c468a6b28feedf95d49f83b35", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0", - "psr/container": "^1.0 || ^2.0", - "symfony/deprecation-contracts": "^2.2" - }, - "conflict": { - "fzaninotto/faker": "*" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.4.1", - "ext-intl": "*", - "symfony/phpunit-bridge": "^4.4 || ^5.2" - }, - "suggest": { - "ext-curl": "Required by Faker\\Provider\\Image to download images.", - "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", - "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", - "ext-mbstring": "Required for multibyte Unicode string functionality." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "v1.16-dev" - } - }, - "autoload": { - "psr-4": { - "Faker\\": "src/Faker/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "François Zaninotto" - } - ], - "description": "Faker is a PHP library that generates fake data for you.", - "keywords": [ - "data", - "faker", - "fixtures" - ], - "support": { - "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.16.0" - }, - "time": "2021-09-06T14:53:37+00:00" - }, { "name": "felixfbecker/advanced-json-rpc", "version": "v3.2.1", @@ -2672,54 +2635,6 @@ }, "time": "2016-08-06T20:24:11+00:00" }, - { - "name": "psr/container", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", - "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", - "shasum": "" - }, - "require": { - "php": ">=7.2.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.1" - }, - "time": "2021-03-05T17:36:06+00:00" - }, { "name": "psr/event-dispatcher", "version": "1.0.0", @@ -3882,73 +3797,6 @@ ], "time": "2021-11-21T19:41:05+00:00" }, - { - "name": "symfony/deprecation-contracts", - "version": "v2.5.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/6f981ee24cf69ee7ce9736146d1c57c2780598a8", - "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-07-12T14:48:14+00:00" - }, { "name": "symfony/event-dispatcher", "version": "v5.3.11", diff --git a/psalm-baseline.xml b/psalm-baseline.xml index fcc3c81..9a8a401 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,2 +1,80 @@ - + + + + $value + + + + + \Generator<string, array{0: bool}> + \Generator<string, array{0: bool}> + \Generator<string, array{0: bool}> + + + + + \Generator<string, array{0: float}> + \Generator<string, array{0: float}> + \Generator<string, array{0: float}> + \Generator<string, array{0: float}> + \Generator<string, array{0: float}> + \Generator<string, array{0: float}> + \Generator<string, array{0: float}> + + + + + \Generator<string, array{0: int}> + \Generator<string, array{0: int}> + \Generator<string, array{0: int}> + \Generator<string, array{0: int}> + \Generator<string, array{0: int}> + \Generator<string, array{0: int}> + \Generator<string, array{0: int}> + + + + + \Generator<string, array{0: null}> + + + + + \Generator<string, array{0: bool}> + + + + + \Generator<string, array{0: resource}> + + + + + \Generator<string, array{0: string}> + \Generator<string, array{0: string}> + \Generator<string, array{0: string}> + \Generator<string, array{0: string}> + \Generator<string, array{0: string}> + \Generator<string, array{0: string}> + + + + + $value + + + assertIsArray + + + + + $value + + + + + $value + + + diff --git a/src/AbstractProvider.php b/src/AbstractProvider.php new file mode 100644 index 0000000..90eeadf --- /dev/null +++ b/src/AbstractProvider.php @@ -0,0 +1,82 @@ + + */ + static $fakers = []; + + if (!\array_key_exists($locale, $fakers)) { + $faker = Factory::create($locale); + + $faker->seed(9001); + + $fakers[$locale] = $faker; + } + + return $fakers[$locale]; + } + + /** + * @param array $values + * + * @throws Exception\EmptyValues + * + * @return \Generator + */ + final protected static function provideDataForValues(array $values): \Generator + { + if ([] === $values) { + throw Exception\EmptyValues::create(); + } + + foreach ($values as $key => $value) { + yield $key => [ + $value, + ]; + } + } + + /** + * @param array $values + * + * @throws Exception\EmptyValues + * + * @return \Generator + */ + final protected static function provideDataForValuesWhere(array $values, \Closure $test): \Generator + { + if ([] === $values) { + throw Exception\EmptyValues::create(); + } + + $filtered = \array_filter($values, static function ($value) use ($test): bool { + return true === $test($value); + }); + + if ([] === $filtered) { + throw Exception\EmptyValues::filtered(); + } + + yield from self::provideDataForValues($filtered); + } +} diff --git a/src/BoolProvider.php b/src/BoolProvider.php new file mode 100644 index 0000000..8dd244b --- /dev/null +++ b/src/BoolProvider.php @@ -0,0 +1,56 @@ + + */ + public static function arbitrary(): \Generator + { + yield from self::provideDataForValues(self::values()); + } + + /** + * @return \Generator + */ + public static function false(): \Generator + { + yield from self::provideDataForValuesWhere(self::values(), static function (bool $value): bool { + return false === $value; + }); + } + + /** + * @return \Generator + */ + public static function true(): \Generator + { + yield from self::provideDataForValuesWhere(self::values(), static function (bool $value): bool { + return $value; + }); + } + + /** + * @return array + */ + private static function values(): array + { + return [ + 'bool-false' => false, + 'bool-true' => true, + ]; + } +} diff --git a/src/Exception/EmptyValues.php b/src/Exception/EmptyValues.php new file mode 100644 index 0000000..a43139d --- /dev/null +++ b/src/Exception/EmptyValues.php @@ -0,0 +1,27 @@ + + */ + public static function arbitrary(): \Generator + { + yield from self::provideDataForValues(self::values()); + } + + /** + * @return \Generator + */ + public static function lessThanZero(): \Generator + { + yield from self::provideDataForValuesWhere(self::values(), static function (float $value): bool { + return 0.0 > $value; + }); + } + + /** + * @return \Generator + */ + public static function zero(): \Generator + { + yield from self::provideDataForValuesWhere(self::values(), static function (float $value): bool { + return 0.0 === $value; + }); + } + + /** + * @return \Generator + */ + public static function greaterThanZero(): \Generator + { + yield from self::provideDataForValuesWhere(self::values(), static function (float $value): bool { + return 0.0 < $value; + }); + } + + /** + * @return \Generator + */ + public static function lessThanOne(): \Generator + { + yield from self::provideDataForValuesWhere(self::values(), static function (float $value): bool { + return 1.0 > $value; + }); + } + + /** + * @return \Generator + */ + public static function one(): \Generator + { + yield from self::provideDataForValuesWhere(self::values(), static function (float $value): bool { + return 1.0 === $value; + }); + } + + /** + * @return \Generator + */ + public static function greaterThanOne(): \Generator + { + yield from self::provideDataForValuesWhere(self::values(), static function (float $value): bool { + return 1.0 < $value; + }); + } + + /** + * @return array + */ + private static function values(): array + { + $faker = self::faker(); + + return [ + 'float-less-than-minus-one' => -0.01 - $faker->randomFloat(null, 1), + 'float-minus-one' => -1.0, + 'float-zero' => 0.0, + 'float-plus-one' => 1.0, + 'float-greater-than-plus-one' => 0.01 + $faker->randomFloat(null, 1), + ]; + } +} diff --git a/src/IntProvider.php b/src/IntProvider.php new file mode 100644 index 0000000..4a05092 --- /dev/null +++ b/src/IntProvider.php @@ -0,0 +1,101 @@ + + */ + public static function arbitrary(): \Generator + { + yield from self::provideDataForValues(self::values()); + } + + /** + * @return \Generator + */ + public static function lessThanZero(): \Generator + { + yield from self::provideDataForValuesWhere(self::values(), static function (int $value): bool { + return 0 > $value; + }); + } + + /** + * @return \Generator + */ + public static function zero(): \Generator + { + yield from self::provideDataForValuesWhere(self::values(), static function (int $value): bool { + return 0 === $value; + }); + } + + /** + * @return \Generator + */ + public static function greaterThanZero(): \Generator + { + yield from self::provideDataForValuesWhere(self::values(), static function (int $value): bool { + return 0 < $value; + }); + } + + /** + * @return \Generator + */ + public static function lessThanOne(): \Generator + { + yield from self::provideDataForValuesWhere(self::values(), static function (int $value): bool { + return 1 > $value; + }); + } + + /** + * @return \Generator + */ + public static function one(): \Generator + { + yield from self::provideDataForValuesWhere(self::values(), static function (int $value): bool { + return 1 === $value; + }); + } + + /** + * @return \Generator + */ + public static function greaterThanOne(): \Generator + { + yield from self::provideDataForValuesWhere(self::values(), static function (int $value): bool { + return 1 < $value; + }); + } + + /** + * @return array + */ + private static function values(): array + { + $faker = self::faker(); + + return [ + 'int-less-than-minus-one' => -1 * $faker->numberBetween(1), + 'int-minus-one' => -1, + 'int-zero' => 0, + 'int-plus-one' => 1, + 'int-greater-than-plus-one' => $faker->numberBetween(1), + ]; + } +} diff --git a/src/Example.php b/src/NullProvider.php similarity index 51% rename from src/Example.php rename to src/NullProvider.php index 1517c0a..10587c7 100644 --- a/src/Example.php +++ b/src/NullProvider.php @@ -13,22 +13,15 @@ namespace Ergebnis\DataProvider; -final class Example +final class NullProvider extends AbstractProvider { - private $name; - - private function __construct(string $name) - { - $this->name = $name; - } - - public static function fromName(string $name): self - { - return new self($name); - } - - public function name(): string + /** + * @return \Generator + */ + public static function null(): \Generator { - return $this->name; + yield from self::provideDataForValues([ + 'null' => null, + ]); } } diff --git a/src/ObjectProvider.php b/src/ObjectProvider.php new file mode 100644 index 0000000..9bb8b2e --- /dev/null +++ b/src/ObjectProvider.php @@ -0,0 +1,27 @@ + + */ + public static function object(): \Generator + { + yield from self::provideDataForValues([ + 'object' => new \stdClass(), + ]); + } +} diff --git a/src/ResourceProvider.php b/src/ResourceProvider.php new file mode 100644 index 0000000..eb7193c --- /dev/null +++ b/src/ResourceProvider.php @@ -0,0 +1,27 @@ + + */ + public static function resource(): \Generator + { + yield from self::provideDataForValues([ + 'resource' => \fopen(__FILE__, 'rb'), + ]); + } +} diff --git a/src/StringProvider.php b/src/StringProvider.php new file mode 100644 index 0000000..fb85eb8 --- /dev/null +++ b/src/StringProvider.php @@ -0,0 +1,173 @@ + + */ + public static function arbitrary(): \Generator + { + yield from self::provideDataForValues(self::values()); + } + + /** + * @return \Generator + */ + public static function blank(): \Generator + { + yield from self::provideDataForValuesWhere(self::values(), static function (string $value): bool { + return '' === \trim($value) + && '' !== $value; + }); + } + + /** + * @return \Generator + */ + public static function empty(): \Generator + { + yield from self::provideDataForValuesWhere(self::values(), static function (string $value): bool { + return '' === $value; + }); + } + + /** + * @return \Generator + */ + public static function trimmed(): \Generator + { + yield from self::provideDataForValuesWhere(self::values(), static function (string $value): bool { + return \trim($value) === $value + && '' !== \trim($value); + }); + } + + /** + * @return \Generator + */ + public static function untrimmed(): \Generator + { + yield from self::provideDataForValuesWhere(self::values(), static function (string $value): bool { + return \trim($value) !== $value + && '' !== \trim($value); + }); + } + + /** + * @return \Generator + */ + public static function withWhitespace(): \Generator + { + yield from self::provideDataForValuesWhere(self::values(), static function (string $value): bool { + return \trim($value) === $value + && 1 === \preg_match('/\s/', $value); + }); + } + + /** + * @return array + */ + private static function values(): array + { + $faker = self::faker(); + + $arbitraryValues = [ + 'string-arbitrary-sentence' => $faker->sentence(), + 'string-arbitrary-word' => $faker->word(), + ]; + + $whitespaceCharacters = self::whitespaceCharacters(); + + /** @var array $blankValues */ + $blankValues = \array_combine( + \array_map(static function (string $key): string { + return \sprintf( + 'string-blank-%s', + $key, + ); + }, \array_keys($whitespaceCharacters)), + $whitespaceCharacters, + ); + + $emptyValues = [ + 'string-empty' => '', + ]; + + /** @var array $untrimmedValues */ + $untrimmedValues = \array_combine( + \array_map(static function (string $key): string { + return \sprintf( + 'string-untrimmed-%s', + $key, + ); + }, \array_keys($whitespaceCharacters)), + \array_map(static function (string $whitespaceCharacter) use ($faker): string { + return \sprintf( + '%s%s%s', + \str_repeat( + $whitespaceCharacter, + $faker->numberBetween(1, 5), + ), + $faker->word(), + \str_repeat( + $whitespaceCharacter, + $faker->numberBetween(1, 5), + ), + ); + }, $whitespaceCharacters), + ); + + /** @var array $withWhitespaceValues */ + $withWhitespaceValues = \array_combine( + \array_map(static function (string $key): string { + return \sprintf( + 'string-with-whitespace-%s', + $key, + ); + }, \array_keys($whitespaceCharacters)), + \array_map(static function (string $whitespaceCharacter) use ($faker): string { + /** @var array $words */ + $words = $faker->words($faker->numberBetween(2, 5)); + + return \implode( + $whitespaceCharacter, + $words, + ); + }, $whitespaceCharacters), + ); + + return \array_merge( + $arbitraryValues, + $blankValues, + $emptyValues, + $untrimmedValues, + $withWhitespaceValues, + ); + } + + /** + * @return array + */ + private static function whitespaceCharacters(): array + { + return [ + 'carriage-return' => "\r", + 'line-feed' => "\n", + 'space' => ' ', + 'tab' => "\t", + ]; + } +} diff --git a/test/Unit/AbstractProviderTestCase.php b/test/Unit/AbstractProviderTestCase.php new file mode 100644 index 0000000..743ccea --- /dev/null +++ b/test/Unit/AbstractProviderTestCase.php @@ -0,0 +1,118 @@ + $values + * @param \Generator> $provider + */ + final protected static function assertProvidesDataSetsForValues(array $values, \Generator $provider): void + { + self::assertExpectedValuesAreNotEmpty($values); + + $expectedDataSets = \array_map(static function ($value): array { + return [ + $value, + ]; + }, $values); + + $actualDataSets = \iterator_to_array($provider); + + self::assertDataSetsAreNotEmpty($actualDataSets); + + self::assertEquals( + $expectedDataSets, + $actualDataSets, + 'Failed asserting that a generator yields data sets for the expected values.', + ); + } + + /** + * @param array $specifications + * @param \Generator> $provider + */ + final protected static function assertProvidesDataSetsForValuesSatisfyingSpecifications(array $specifications, \Generator $provider): void + { + self::assertContainsOnly( + 'string', + \array_keys($specifications), + true, + 'Failed asserting that the keys of specifications are all strings.', + ); + + self::assertContainsOnly( + Test\Util\Specification\Specification::class, + $specifications, + false, + \sprintf( + 'Failed asserting that the values of specifications implement "%s".', + Test\Util\Specification\Specification::class, + ), + ); + + $dataSets = \iterator_to_array($provider); + + self::assertEquals( + \array_keys($specifications), + \array_keys($dataSets), + 'Failed asserting that the provided data has the same keys as the specifications.', + ); + + $keysForDataSetsWhereValueDoesNotSatisfySpecification = \array_filter(\array_keys($dataSets), static function (string $key) use ($dataSets, $specifications): bool { + $specification = $specifications[$key]; + + $dataSet = $dataSets[$key]; + + self::assertIsArray($dataSet, \sprintf( + 'Failed asserting that the data set provided for key "%s" is an array.', + $key, + )); + + self::assertCount(1, $dataSet, \sprintf( + 'Failed asserting that the data set provided for key "%s" contains only one value.', + $key, + )); + + $value = \array_shift($dataSet); + + return !$specification->isSatisfiedBy($value); + }); + + self::assertEquals([], $keysForDataSetsWhereValueDoesNotSatisfySpecification, \sprintf( + 'Failed asserting that the value for the data sets with the keys "%s" satisfy the corresponding requirements.', + \implode( + '", "', + $keysForDataSetsWhereValueDoesNotSatisfySpecification, + ), + )); + } + + final protected static function assertDataSetsAreNotEmpty(array $actual): void + { + self::assertNotEmpty($actual, 'Failed asserting that provided data sets are not empty.'); + } + + private static function assertExpectedValuesAreNotEmpty(array $values): void + { + self::assertNotEmpty($values, 'Failed asserting that expected values are not empty.'); + } +} diff --git a/test/Unit/BoolProviderTest.php b/test/Unit/BoolProviderTest.php new file mode 100644 index 0000000..b9f0799 --- /dev/null +++ b/test/Unit/BoolProviderTest.php @@ -0,0 +1,90 @@ + Test\Util\Specification\Identical::create(false), + 'bool-true' => Test\Util\Specification\Identical::create(true), + ]; + + $provider = BoolProvider::arbitrary(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } + + /** + * @dataProvider \Ergebnis\DataProvider\BoolProvider::false() + * + * @param mixed $value + */ + public function testFalseProvidesFalse($value): void + { + self::assertFalse($value); + } + + public function testFalseReturnsGeneratorThatProvidesFalse(): void + { + $specifications = [ + 'bool-false' => Test\Util\Specification\Identical::create(false), + ]; + + $provider = BoolProvider::false(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } + + /** + * @dataProvider \Ergebnis\DataProvider\BoolProvider::true() + * + * @param mixed $value + */ + public function testTrueProvidesTrue($value): void + { + self::assertTrue($value); + } + + public function testTrueReturnsGeneratorThatProvidesTrue(): void + { + $specifications = [ + 'bool-true' => Test\Util\Specification\Identical::create(true), + ]; + + $provider = BoolProvider::true(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } +} diff --git a/test/Unit/ExampleTest.php b/test/Unit/ExampleTest.php deleted file mode 100644 index 3034617..0000000 --- a/test/Unit/ExampleTest.php +++ /dev/null @@ -1,37 +0,0 @@ -sentence; - - $example = DataProvider\Example::fromName($name); - - self::assertSame($name, $example->name()); - } -} diff --git a/test/Unit/Exception/EmptyValuesTest.php b/test/Unit/Exception/EmptyValuesTest.php new file mode 100644 index 0000000..4b79118 --- /dev/null +++ b/test/Unit/Exception/EmptyValuesTest.php @@ -0,0 +1,39 @@ +getMessage()); + } + + public function testFilteredReturnsException(): void + { + $exception = Exception\EmptyValues::filtered(); + + self::assertSame('Filtered values can not be empty.', $exception->getMessage()); + } +} diff --git a/test/Unit/FloatProviderTest.php b/test/Unit/FloatProviderTest.php new file mode 100644 index 0000000..01a2460 --- /dev/null +++ b/test/Unit/FloatProviderTest.php @@ -0,0 +1,173 @@ + Test\Util\Specification\Closure::create(static function (float $value): bool { + return -1.0 > $value; + }), + 'float-minus-one' => Test\Util\Specification\Identical::create(-1.0), + 'float-zero' => Test\Util\Specification\Identical::create(0.0), + 'float-plus-one' => Test\Util\Specification\Identical::create(1.0), + 'float-greater-than-plus-one' => Test\Util\Specification\Closure::create(static function (float $value): bool { + return 1.0 < $value; + }), + ]; + + $provider = FloatProvider::arbitrary(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } + + /** + * @dataProvider \Ergebnis\DataProvider\FloatProvider::lessThanZero() + */ + public function testLessThanZeroProvidesFloatLessThanZero(float $value): void + { + self::assertLessThan(0, $value); + } + + public function testLessThanZeroReturnsGeneratorThatProvidesFloatLessThanZero(): void + { + $specifications = [ + 'float-less-than-minus-one' => Test\Util\Specification\Closure::create(static function (float $value): bool { + return -1.0 > $value; + }), + 'float-minus-one' => Test\Util\Specification\Identical::create(-1.0), + ]; + + $provider = FloatProvider::lessThanZero(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } + + /** + * @dataProvider \Ergebnis\DataProvider\FloatProvider::zero() + */ + public function testZeroProvidesZero(float $value): void + { + self::assertSame(0.0, $value); + } + + public function testZeroReturnsGeneratorThatProvidesZero(): void + { + $specifications = [ + 'float-zero' => Test\Util\Specification\Identical::create(0.0), + ]; + + $provider = FloatProvider::zero(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } + + /** + * @dataProvider \Ergebnis\DataProvider\FloatProvider::greaterThanZero() + */ + public function testGreaterThanZeroProvidesFloatGreaterThanZero(float $value): void + { + self::assertGreaterThan(0.0, $value); + } + + public function testGreaterThanZeroReturnsGeneratorThatProvidesFloatGreaterThanZero(): void + { + $specifications = [ + 'float-plus-one' => Test\Util\Specification\Identical::create(1.0), + 'float-greater-than-plus-one' => Test\Util\Specification\Closure::create(static function (float $value): bool { + return 1.0 < $value; + }), + ]; + + $provider = FloatProvider::greaterThanZero(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } + + /** + * @dataProvider \Ergebnis\DataProvider\FloatProvider::lessThanOne() + */ + public function testLessThanOneProvidesFloatLessThanOne(float $value): void + { + self::assertLessThan(1, $value); + } + + public function testLessThanOneReturnsGeneratorThatProvidesFloatLessThanOne(): void + { + $specifications = [ + 'float-less-than-minus-one' => Test\Util\Specification\Closure::create(static function (float $value): bool { + return -1.0 > $value; + }), + 'float-minus-one' => Test\Util\Specification\Identical::create(-1.0), + 'float-zero' => Test\Util\Specification\Identical::create(0.0), + ]; + + $provider = FloatProvider::lessThanOne(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } + + public function testOneReturnsGeneratorThatProvidesOne(): void + { + $specifications = [ + 'float-plus-one' => Test\Util\Specification\Identical::create(1.0), + ]; + + $provider = FloatProvider::one(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } + + /** + * @dataProvider \Ergebnis\DataProvider\FloatProvider::greaterThanOne() + */ + public function testGreaterThanOneProvidesFloatGreaterThanOne(float $value): void + { + self::assertGreaterThan(1, $value); + } + + public function testGreaterThanOneReturnsGeneratorThatProvidesFloatGreaterThanOne(): void + { + $specifications = [ + 'float-greater-than-plus-one' => Test\Util\Specification\Closure::create(static function (float $value): bool { + return 1.0 < $value; + }), + ]; + + $provider = FloatProvider::greaterThanOne(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } +} diff --git a/test/Unit/IntProviderTest.php b/test/Unit/IntProviderTest.php new file mode 100644 index 0000000..8a46059 --- /dev/null +++ b/test/Unit/IntProviderTest.php @@ -0,0 +1,173 @@ + Test\Util\Specification\Closure::create(static function (int $value): bool { + return -1 > $value; + }), + 'int-minus-one' => Test\Util\Specification\Identical::create(-1), + 'int-zero' => Test\Util\Specification\Identical::create(0), + 'int-plus-one' => Test\Util\Specification\Identical::create(1), + 'int-greater-than-plus-one' => Test\Util\Specification\Closure::create(static function (int $value): bool { + return 1 < $value; + }), + ]; + + $provider = IntProvider::arbitrary(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } + + /** + * @dataProvider \Ergebnis\DataProvider\IntProvider::lessThanZero() + */ + public function testLessThanZeroProvidesIntLessThanZero(int $value): void + { + self::assertLessThan(0, $value); + } + + public function testLessThanZeroReturnsGeneratorThatProvidesIntLessThanZero(): void + { + $specifications = [ + 'int-less-than-minus-one' => Test\Util\Specification\Closure::create(static function (int $value): bool { + return -1 > $value; + }), + 'int-minus-one' => Test\Util\Specification\Identical::create(-1), + ]; + + $provider = IntProvider::lessThanZero(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } + + /** + * @dataProvider \Ergebnis\DataProvider\IntProvider::zero() + */ + public function testZeroProvidesZero(int $value): void + { + self::assertSame(0, $value); + } + + public function testZeroReturnsGeneratorThatProvidesZero(): void + { + $specifications = [ + 'int-zero' => Test\Util\Specification\Identical::create(0), + ]; + + $provider = IntProvider::zero(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } + + /** + * @dataProvider \Ergebnis\DataProvider\IntProvider::greaterThanZero() + */ + public function testGreaterThanZeroProvidesIntGreaterThanZero(int $value): void + { + self::assertGreaterThan(0, $value); + } + + public function testGreaterThanZeroReturnsGeneratorThatProvidesIntGreaterThanZero(): void + { + $specifications = [ + 'int-plus-one' => Test\Util\Specification\Identical::create(1), + 'int-greater-than-plus-one' => Test\Util\Specification\Closure::create(static function (int $value): bool { + return 1 < $value; + }), + ]; + + $provider = IntProvider::greaterThanZero(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } + + /** + * @dataProvider \Ergebnis\DataProvider\IntProvider::lessThanOne() + */ + public function testLessThanOneProvidesIntLessThanOne(int $value): void + { + self::assertLessThan(1, $value); + } + + public function testLessThanOneReturnsGeneratorThatProvidesIntLessThanOne(): void + { + $specifications = [ + 'int-less-than-minus-one' => Test\Util\Specification\Closure::create(static function (int $value): bool { + return -1 > $value; + }), + 'int-minus-one' => Test\Util\Specification\Identical::create(-1), + 'int-zero' => Test\Util\Specification\Identical::create(0), + ]; + + $provider = IntProvider::lessThanOne(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } + + public function testOneReturnsGeneratorThatProvidesOne(): void + { + $specifications = [ + 'int-plus-one' => Test\Util\Specification\Identical::create(1), + ]; + + $provider = IntProvider::one(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } + + /** + * @dataProvider \Ergebnis\DataProvider\IntProvider::greaterThanOne() + */ + public function testGreaterThanOneProvidesIntGreaterThanOne(int $value): void + { + self::assertGreaterThan(1, $value); + } + + public function testGreaterThanOneReturnsGeneratorThatProvidesIntGreaterThanOne(): void + { + $specifications = [ + 'int-greater-than-plus-one' => Test\Util\Specification\Closure::create(static function (int $value): bool { + return 1 < $value; + }), + ]; + + $provider = IntProvider::greaterThanOne(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } +} diff --git a/test/Unit/NullProviderTest.php b/test/Unit/NullProviderTest.php new file mode 100644 index 0000000..d1f5d4e --- /dev/null +++ b/test/Unit/NullProviderTest.php @@ -0,0 +1,47 @@ + Test\Util\Specification\Identical::create(null), + ]; + + $provider = NullProvider::null(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } +} diff --git a/test/Unit/ObjectProviderTest.php b/test/Unit/ObjectProviderTest.php new file mode 100644 index 0000000..f4dc0a9 --- /dev/null +++ b/test/Unit/ObjectProviderTest.php @@ -0,0 +1,47 @@ + Test\Util\Specification\Equal::create(new \stdClass()), + ]; + + $provider = ObjectProvider::object(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } +} diff --git a/test/Unit/ResourceProviderTest.php b/test/Unit/ResourceProviderTest.php new file mode 100644 index 0000000..a0fabeb --- /dev/null +++ b/test/Unit/ResourceProviderTest.php @@ -0,0 +1,49 @@ + Test\Util\Specification\Closure::create(static function ($value): bool { + return \is_resource($value); + }), + ]; + + $provider = ResourceProvider::resource(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } +} diff --git a/test/Unit/StringProviderTest.php b/test/Unit/StringProviderTest.php new file mode 100644 index 0000000..54a495f --- /dev/null +++ b/test/Unit/StringProviderTest.php @@ -0,0 +1,178 @@ + Test\Util\Specification\Closure::create(static function (string $value): bool { + return '' !== $value && '' !== \trim($value); + }), + 'string-arbitrary-word' => Test\Util\Specification\Closure::create(static function (string $value): bool { + return '' !== $value && '' !== \trim($value); + }), + 'string-blank-carriage-return' => Test\Util\Specification\Identical::create("\r"), + 'string-blank-line-feed' => Test\Util\Specification\Identical::create("\n"), + 'string-blank-space' => Test\Util\Specification\Identical::create(' '), + 'string-blank-tab' => Test\Util\Specification\Identical::create("\t"), + 'string-empty' => Test\Util\Specification\Identical::create(''), + 'string-untrimmed-carriage-return' => Test\Util\Specification\Pattern::create('/^\r{1,5}\w+\r{1,5}$/'), + 'string-untrimmed-line-feed' => Test\Util\Specification\Pattern::create('/^\n{1,5}\w+\n{1,5}$/'), + 'string-untrimmed-space' => Test\Util\Specification\Pattern::create('/^\s{1,5}\w+\s{1,5}$/'), + 'string-untrimmed-tab' => Test\Util\Specification\Pattern::create('/^\t{1,5}\w+\t{1,5}$/'), + 'string-with-whitespace-carriage-return' => Test\Util\Specification\Pattern::create('/^\w+(\r+\w+){1,4}$/'), + 'string-with-whitespace-line-feed' => Test\Util\Specification\Pattern::create('/^\w+(\n+\w+){1,4}$/'), + 'string-with-whitespace-space' => Test\Util\Specification\Pattern::create('/^\w+(\s+\w+){1,4}$/'), + 'string-with-whitespace-tab' => Test\Util\Specification\Pattern::create('/^\w+(\t+\w+){1,4}$/'), + ]; + + $provider = StringProvider::arbitrary(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } + + /** + * @dataProvider \Ergebnis\DataProvider\StringProvider::blank() + */ + public function testBlankProvidesBlankString(string $value): void + { + self::assertSame('', \trim($value)); + self::assertNotSame('', $value); + } + + public function testBlankReturnsGeneratorThatProvidesStringsThatAreNeitherEmptyNorBlank(): void + { + $specifications = [ + 'string-blank-carriage-return' => Test\Util\Specification\Identical::create("\r"), + 'string-blank-line-feed' => Test\Util\Specification\Identical::create("\n"), + 'string-blank-space' => Test\Util\Specification\Identical::create(' '), + 'string-blank-tab' => Test\Util\Specification\Identical::create("\t"), + ]; + + $provider = StringProvider::blank(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } + + /** + * @dataProvider \Ergebnis\DataProvider\StringProvider::empty() + */ + public function testEmptyProvidesEmptyString(string $value): void + { + self::assertSame('', $value); + } + + public function testEmptyReturnsGeneratorThatProvidesAnEmptyString(): void + { + $specifications = [ + 'string-empty' => Test\Util\Specification\Identical::create(''), + ]; + + $provider = StringProvider::empty(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } + + /** + * @dataProvider \Ergebnis\DataProvider\StringProvider::untrimmed() + */ + public function testUntrimmedProvidesUntrimmedString(string $value): void + { + self::assertNotSame(\trim($value), $value); + self::assertNotSame('', $value); + self::assertNotSame('', \trim($value)); + } + + /** + * @dataProvider \Ergebnis\DataProvider\StringProvider::trimmed() + * + * @param mixed $value + */ + public function testTrimmedProvidesString($value): void + { + self::assertIsString($value); + } + + public function testTrimmedReturnsGeneratorThatProvidesStringsThatAreTrimmed(): void + { + $specifications = [ + 'string-arbitrary-sentence' => Test\Util\Specification\Closure::create(static function (string $value): bool { + return '' !== $value && '' !== \trim($value); + }), + 'string-arbitrary-word' => Test\Util\Specification\Closure::create(static function (string $value): bool { + return '' !== $value && '' !== \trim($value); + }), + 'string-with-whitespace-carriage-return' => Test\Util\Specification\Pattern::create('/^\w+(\r+\w+){1,4}$/'), + 'string-with-whitespace-line-feed' => Test\Util\Specification\Pattern::create('/^\w+(\n+\w+){1,4}$/'), + 'string-with-whitespace-space' => Test\Util\Specification\Pattern::create('/^\w+(\s+\w+){1,4}$/'), + 'string-with-whitespace-tab' => Test\Util\Specification\Pattern::create('/^\w+(\t+\w+){1,4}$/'), + ]; + + $provider = StringProvider::trimmed(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } + + public function testUntrimmedReturnsGeneratorThatProvidesUntrimmedStrings(): void + { + $specifications = [ + 'string-untrimmed-carriage-return' => Test\Util\Specification\Pattern::create('/^\r{1,5}\w+\r{1,5}$/'), + 'string-untrimmed-line-feed' => Test\Util\Specification\Pattern::create('/^\n{1,5}\w+\n{1,5}$/'), + 'string-untrimmed-space' => Test\Util\Specification\Pattern::create('/^\s{1,5}\w+\s{1,5}$/'), + 'string-untrimmed-tab' => Test\Util\Specification\Pattern::create('/^\t{1,5}\w+\t{1,5}$/'), + ]; + + $provider = StringProvider::untrimmed(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } + + public function testWithWhitespaceReturnsGeneratorThatProvidesTrimmedStringsWithWhitespace(): void + { + $specifications = [ + 'string-arbitrary-sentence' => Test\Util\Specification\Closure::create(static function (string $value): bool { + return '' !== $value && '' !== \trim($value); + }), + 'string-with-whitespace-carriage-return' => Test\Util\Specification\Pattern::create('/^\w+(\r+\w+){1,4}$/'), + 'string-with-whitespace-line-feed' => Test\Util\Specification\Pattern::create('/^\w+(\n+\w+){1,4}$/'), + 'string-with-whitespace-space' => Test\Util\Specification\Pattern::create('/^\w+(\s+\w+){1,4}$/'), + 'string-with-whitespace-tab' => Test\Util\Specification\Pattern::create('/^\w+(\t+\w+){1,4}$/'), + ]; + + $provider = StringProvider::withWhitespace(); + + self::assertProvidesDataSetsForValuesSatisfyingSpecifications($specifications, $provider); + } +} diff --git a/test/Util/Specification/Closure.php b/test/Util/Specification/Closure.php new file mode 100644 index 0000000..582a5b1 --- /dev/null +++ b/test/Util/Specification/Closure.php @@ -0,0 +1,53 @@ +closure = static function ($value) use ($closure): bool { + return true === $closure($value); + }; + } + + public static function create(\Closure $closure): self + { + return new self($closure); + } + + public function isSatisfiedBy($value): bool + { + $closure = $this->closure; + + $isSatisfiedBy = $closure($value); + + if (!\is_bool($isSatisfiedBy)) { + throw new \RuntimeException(\sprintf( + 'Closure should return bool, got "%s" instead.', + \is_object($value) ? \get_class($value) : \gettype($value), + )); + } + + return $isSatisfiedBy; + } +} diff --git a/test/Util/Specification/Equal.php b/test/Util/Specification/Equal.php new file mode 100644 index 0000000..80ec14d --- /dev/null +++ b/test/Util/Specification/Equal.php @@ -0,0 +1,43 @@ +value = $value; + } + + /** + * @param mixed $value + */ + public static function create($value): self + { + return new self($value); + } + + public function isSatisfiedBy($value): bool + { + return $this->value == $value; + } +} diff --git a/test/Util/Specification/Identical.php b/test/Util/Specification/Identical.php new file mode 100644 index 0000000..ec1dc02 --- /dev/null +++ b/test/Util/Specification/Identical.php @@ -0,0 +1,46 @@ +value = $value; + } + + /** + * @param mixed $value + */ + public static function create($value): self + { + return new self($value); + } + + public function isSatisfiedBy($value): bool + { + return $this->value === $value; + } +} diff --git a/test/Util/Specification/Pattern.php b/test/Util/Specification/Pattern.php new file mode 100644 index 0000000..7156f10 --- /dev/null +++ b/test/Util/Specification/Pattern.php @@ -0,0 +1,41 @@ +pattern = $pattern; + } + + public static function create(string $pattern): self + { + return new self($pattern); + } + + public function isSatisfiedBy($value): bool + { + if (!\is_string($value)) { + return false; + } + + return 1 === \preg_match($this->pattern, $value); + } +} diff --git a/test/Util/Specification/Specification.php b/test/Util/Specification/Specification.php new file mode 100644 index 0000000..0589cbd --- /dev/null +++ b/test/Util/Specification/Specification.php @@ -0,0 +1,22 @@ +