diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d650d88..2f3eecb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,95 +1,14 @@ name: CI -on: [push] +on: [push, pull_request] jobs: - phpunit: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, macOS-latest] - php-version: ['8.2', '8.3'] - dependencies: ['lowest', 'highest'] - name: 'PHPUnit' - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-version }} - extensions: mbstring, intl - coverage: none - - name: Composer - uses: "ramsey/composer-install@v2" - with: - dependency-versions: ${{ matrix.dependencies }} - - name: PHPUnit - run: vendor/bin/phpunit + blackbox: + uses: innmind/github-workflows/.github/workflows/black-box-matrix.yml@main coverage: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, macOS-latest] - php-version: ['8.2', '8.3'] - dependencies: ['lowest', 'highest'] - name: 'Coverage' - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-version }} - extensions: mbstring, intl - coverage: xdebug - - name: Composer - uses: "ramsey/composer-install@v2" - with: - dependency-versions: ${{ matrix.dependencies }} - - name: PHPUnit - run: vendor/bin/phpunit --coverage-clover=coverage.clover - env: - BLACKBOX_SET_SIZE: 1 - - uses: codecov/codecov-action@v1 - with: - token: ${{ secrets.CODECOV_TOKEN }} + uses: innmind/github-workflows/.github/workflows/coverage-matrix.yml@main + secrets: inherit psalm: - runs-on: ubuntu-latest - strategy: - matrix: - php-version: ['8.2', '8.3'] - dependencies: ['lowest', 'highest'] - name: 'Psalm' - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-version }} - extensions: mbstring, intl - - name: Composer - uses: "ramsey/composer-install@v2" - with: - dependency-versions: ${{ matrix.dependencies }} - - name: Psalm - run: vendor/bin/psalm --shepherd + uses: innmind/github-workflows/.github/workflows/psalm-matrix.yml@main cs: - runs-on: ubuntu-latest - strategy: - matrix: - php-version: ['8.2'] - name: 'CS' - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-version }} - extensions: mbstring, intl - - name: Composer - uses: "ramsey/composer-install@v2" - - name: CS - run: vendor/bin/php-cs-fixer fix --diff --dry-run + uses: innmind/github-workflows/.github/workflows/cs.yml@main diff --git a/.github/workflows/extensive.yml b/.github/workflows/extensive.yml new file mode 100644 index 0000000..257f139 --- /dev/null +++ b/.github/workflows/extensive.yml @@ -0,0 +1,12 @@ +name: Extensive CI + +on: + push: + tags: + - '*' + paths: + - '.github/workflows/extensive.yml' + +jobs: + blackbox: + uses: innmind/github-workflows/.github/workflows/extensive.yml@main diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..b25ad8a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,11 @@ +name: Create release + +on: + push: + tags: + - '*' + +jobs: + release: + uses: innmind/github-workflows/.github/workflows/release.yml@main + secrets: inherit diff --git a/.gitignore b/.gitignore index e96516b..987e2a2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ composer.lock vendor -.phpunit.result.cache diff --git a/CHANGELOG.md b/CHANGELOG.md index b7d0216..2c0de34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## [Unreleased] + +### Added + +- `Innmind\MediaType\MediaType::attempt()` + +### Changed + +- Require PHP `8.4` +- `Innmind\MediaType\MediaType` constructor is now private, use `::from()` instead +- `Innmind\MediaType\Parameter` constructor is now private, use `::from()` instead +- `Innmind\MediaType\MediaType` top level is now represented by `Innmind\MediaType\TopLevel` + +### Removed + +- `Innmind\MediaType\Exception\InvalidTopLevelType` +- `Innmind\MediaType\Exception\Exception` +- `Innmind\MediaType\Exception\DomainException` + ## 2.3.0 - 2025-03-20 ### Added diff --git a/blackbox.php b/blackbox.php new file mode 100644 index 0000000..502a180 --- /dev/null +++ b/blackbox.php @@ -0,0 +1,31 @@ +when( + \getenv('BLACKBOX_SET_SIZE') !== false, + static fn(Application $app) => $app->scenariiPerProof((int) \getenv('BLACKBOX_SET_SIZE')), + ) + ->when( + \getenv('ENABLE_COVERAGE') !== false, + static fn(Application $app) => $app + ->scenariiPerProof(1) + ->codeCoverage( + CodeCoverage::of( + __DIR__.'/src/', + __DIR__.'/tests/', + ) + ->dumpTo('coverage.clover') + ->enableWhen(true), + ), + ) + ->tryToProve(Load::directory(__DIR__.'/tests/')) + ->exit(); diff --git a/composer.json b/composer.json index 8ee5c80..2c2f74a 100644 --- a/composer.json +++ b/composer.json @@ -15,8 +15,8 @@ "issues": "http://github.com/Innmind/MediaType/issues" }, "require": { - "php": "~8.2", - "innmind/immutable": "~4.15|~5.0" + "php": "~8.4", + "innmind/immutable": "~6.0" }, "autoload": { "psr-4": { @@ -30,18 +30,17 @@ } }, "require-dev": { - "phpunit/phpunit": "~10.2|~11.0|~12.0", - "innmind/black-box": "~5.0|^6.0.1", - "vimeo/psalm": "~5.6", + "innmind/static-analysis": "~1.3", + "innmind/black-box": "~6.5", "innmind/coding-standard": "~2.0" }, "conflict": { - "innmind/black-box": "<5.0|~7.0" + "innmind/black-box": "<6.0|~7.0" }, "suggest": { "innmind/black-box": "For property based testing" }, "provide": { - "innmind/black-box-sets": "5.0" + "innmind/black-box-sets": "6.0" } } diff --git a/fixtures/MediaType.php b/fixtures/MediaType.php index 9e3faae..f383316 100644 --- a/fixtures/MediaType.php +++ b/fixtures/MediaType.php @@ -6,60 +6,59 @@ use Innmind\MediaType\{ MediaType as Model, Parameter, + TopLevel, }; use Innmind\BlackBox\Set; final class MediaType { /** - * @return Set + * @return Set\Provider */ - public static function any(): Set + public static function any(): Set\Provider { $alphaNumerical = [...\range('A', 'Z'), ...\range('a', 'z'), ...\range(0, 9)]; - $validChars = Set\Composite::immutable( + $validChars = Set::compose( static fn($first, array $rest): string => \implode('', [$first, ...$rest]), - Set\Elements::of(...$alphaNumerical), - Set\Sequence::of( - Set\Elements::of('!', '#', '$', '&', '^', '_', '.', '-', ...$alphaNumerical), - Set\Integers::between(0, 126), - ), + Set::of(...$alphaNumerical), + Set::sequence( + Set::of('!', '#', '$', '&', '^', '_', '.', '-', ...$alphaNumerical), + )->between(0, 126), ); - return Set\Composite::immutable( + return Set::compose( static function($topLevel, $subType, $suffix, $parameterName, $parameterValue): Model { if ($parameterName) { - return new Model( + return Model::from( $topLevel, $subType, $suffix, - new Parameter( + Parameter::from( $parameterName, $parameterValue, ), ); } - return new Model( + return Model::from( $topLevel, $subType, $suffix, ); }, - Set\Elements::of(...Model::topLevels()->toList()), + Set::of(...TopLevel::cases()), $validChars, - Set\Either::any( - Set\Elements::of(''), - $validChars, - ), - Set\Either::any( + Set::either( + Set::of(''), $validChars, - Set\Elements::of(null), // to generate a type without a parameter ), - Set\Strings::madeOf( - Set\Chars::alphanumerical(), - Set\Elements::of('-', '.'), - )->between(1, 100), + $validChars->nullable(), // to generate a type without a parameter + Set::strings() + ->madeOf( + Set::strings()->chars()->alphanumerical(), + Set::of('-', '.'), + ) + ->between(1, 100), ); } } diff --git a/phpunit.xml.dist b/phpunit.xml.dist deleted file mode 100644 index dc064fb..0000000 --- a/phpunit.xml.dist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - ./tests - - - - - . - - - ./tests - ./vendor - - - diff --git a/src/Exception/DomainException.php b/src/Exception/DomainException.php deleted file mode 100644 index fe19573..0000000 --- a/src/Exception/DomainException.php +++ /dev/null @@ -1,8 +0,0 @@ - */ - private Sequence $parameters; + private function __construct( + private TopLevel $topLevel, + private string $subType, + private string $suffix, + /** @var Sequence */ + private Sequence $parameters, + ) { + } /** + * @psalm-pure * @no-named-arguments */ - public function __construct( - string $topLevel, + public static function from( + TopLevel $topLevel, string $subType, string $suffix = '', Parameter ...$parameters, - ) { - if (!self::topLevels()->contains($topLevel)) { - throw new InvalidTopLevelType($topLevel); - } - + ): self { $format = self::FORMAT; $regex = "~^$format$~"; if (!Str::of($subType)->matches($regex)) { - throw new DomainException($subType); + throw new \DomainException($subType); } if ($suffix !== '' && !Str::of($suffix)->matches($regex)) { - throw new DomainException($suffix); + throw new \DomainException($suffix); } - $this->topLevel = $topLevel; - $this->subType = $subType; - $this->suffix = $suffix; - $this->parameters = Sequence::of(...$parameters); + return new self( + $topLevel, + $subType, + $suffix, + Sequence::of(...$parameters), + ); } /** * @psalm-pure - * @throws DomainException + * + * @throws \DomainException */ public static function of(string $string): self { - return self::maybe($string)->match( - static fn($self) => $self, - static fn() => throw new DomainException($string), - ); + return self::attempt($string)->unwrap(); } /** @@ -81,26 +77,48 @@ public static function maybe(string $string): Maybe ->filter(static fn($string) => $string->matches(self::pattern())) ->map(static fn($string) => $string->pregSplit('~[;,] ?~')) ->flatMap( - static fn($splits) => self::capture($splits->first())->flatMap( - static fn(Str $topLevel, Str $subType, Str $suffix) => self::build( - $topLevel->toString(), - $subType->toString(), - $suffix->toString(), - $splits->drop(1), + static fn($splits) => $splits + ->first() + ->flatMap(self::capture(...)) + ->flatMap( + static fn($self) => $splits + ->drop(1) + ->map(static fn($parameter) => $parameter->toString()) + ->map(Parameter::of(...)) + ->sink($self) + ->maybe(static fn($self, $parameter) => $parameter->map( + $self->withParameter(...), + )), ), - ), ); } + /** + * @psalm-pure + * + * @return Attempt + */ + public static function attempt(string $string): Attempt + { + return self::maybe($string)->attempt( + static fn() => throw new \DomainException($string), + ); + } + /** * @psalm-pure */ public static function null(): self { - return new self('application', 'octet-stream'); + return new self( + TopLevel::application, + 'octet-stream', + '', + Sequence::of(), + ); } - public function topLevel(): string + public function topLevel(): TopLevel { return $this->topLevel; } @@ -132,31 +150,20 @@ public function toString(): string return \sprintf( '%s/%s%s%s', - $this->topLevel, + $this->topLevel->name, $this->subType, $this->suffix !== '' ? '+'.$this->suffix : '', !$parameters->empty() ? '; '.$parameters->toString() : '', ); } - /** - * List of allowed top levels - * - * @return Set - */ - public static function topLevels(): Set + private function withParameter(Parameter $parameter): self { - return Set::strings( - 'application', - 'audio', - 'font', - 'example', - 'image', - 'message', - 'model', - 'multipart', - 'text', - 'video', + return new self( + $this->topLevel, + $this->subType, + $this->suffix, + ($this->parameters)($parameter), ); } @@ -166,68 +173,43 @@ private static function pattern(): string return \sprintf( "~%s/$format(\+$format)?([;,] $format=[\w\-.]+)?~", - Str::of('|')->join(self::topLevels())->toString(), + Str::of('|') + ->join( + Sequence::of(...TopLevel::cases()) + ->map(static fn($level) => $level->name), + ) + ->toString(), ); } /** - * @param Sequence $parameters - * * @return Maybe */ - private static function build( - string $topLevel, - string $subType, - string $suffix, - Sequence $parameters, - ): Maybe { - if ($parameters->empty()) { - return Maybe::just(new self($topLevel, $subType, $suffix)); - } - - /** @psalm-suppress NamedArgumentNotAllowed */ - return self::captureParameters($parameters)->map( - static fn(Parameter ...$parameters) => new self( - $topLevel, - $subType, - $suffix, - ...$parameters, - ), - ); - } - - /** - * @param Maybe $string - */ - private static function capture(Maybe $string): Maybe\Comprehension + private static function capture(Str $string): Maybe { $format = self::FORMAT; - - return $string - ->map(static fn($string) => $string->capture(\sprintf( - "~^(?%s)/(?$format)(\+(?$format))?$~", - Str::of('|')->join(self::topLevels())->toString(), - ))) - ->match( - static fn($matches) => Maybe::all( - $matches->get('topLevel'), - $matches->get('subType'), - $matches->get('suffix')->otherwise(static fn() => Maybe::just(Str::of(''))), - ), - static fn() => Maybe::all(Maybe::nothing()), - ); - } - - /** - * @param Sequence $parameters - */ - private static function captureParameters(Sequence $parameters): Maybe\Comprehension - { - return $parameters - ->map(static fn($parameter) => Parameter::of($parameter->toString())) - ->match( - static fn($first, $rest) => Maybe::all($first, ...$rest->toList()), - static fn() => Maybe::all(Maybe::nothing()), - ); + $matches = $string->capture(\sprintf( + "~^(?%s)/(?$format)(\+(?$format))?$~", + Str::of('|') + ->join( + Sequence::of(...TopLevel::cases()) + ->map(static fn($level) => $level->name), + ) + ->toString(), + )); + + return Maybe::all( + $matches + ->get('topLevel') + ->map(static fn($level) => $level->toString()) + ->flatMap(TopLevel::maybe(...)), + $matches->get('subType'), + $matches->get('suffix')->otherwise(static fn() => Maybe::just(Str::of(''))), + )->map(static fn(TopLevel $topLevel, Str $subType, Str $suffix) => new self( + $topLevel, + $subType->toString(), + $suffix->toString(), + Sequence::of(), + )); } } diff --git a/src/Parameter.php b/src/Parameter.php index f67a379..6316dc0 100644 --- a/src/Parameter.php +++ b/src/Parameter.php @@ -3,7 +3,6 @@ namespace Innmind\MediaType; -use Innmind\MediaType\Exception\DomainException; use Innmind\Immutable\{ Str, Maybe, @@ -22,18 +21,26 @@ final class Parameter private string $name; private string $value; - public function __construct(string $name, string $value) + private function __construct(string $name, string $value) { $format = self::NAME; if (!Str::of($name)->matches("~^$format$~")) { - throw new DomainException($name); + throw new \DomainException($name); } $this->name = $name; $this->value = $value; } + /** + * @psalm-pure + */ + public static function from(string $name, string $value): self + { + return new self($name, $value); + } + /** * @psalm-pure * diff --git a/src/TopLevel.php b/src/TopLevel.php new file mode 100644 index 0000000..ab5abb3 --- /dev/null +++ b/src/TopLevel.php @@ -0,0 +1,45 @@ + + */ + public static function maybe(string $value): Maybe + { + return Maybe::of(match ($value) { + 'application' => self::application, + 'audio' => self::audio, + 'font' => self::font, + 'example' => self::example, + 'image' => self::image, + 'message' => self::message, + 'model' => self::model, + 'multipart' => self::multipart, + 'text' => self::text, + 'video' => self::video, + default => null, + }); + } +} diff --git a/tests/Fixtures/MediaTypeTest.php b/tests/Fixtures/MediaTypeTest.php index ab0a11d..090aa9a 100644 --- a/tests/Fixtures/MediaTypeTest.php +++ b/tests/Fixtures/MediaTypeTest.php @@ -5,8 +5,8 @@ use Fixtures\Innmind\MediaType\MediaType; use Innmind\MediaType\MediaType as Model; -use PHPUnit\Framework\TestCase; use Innmind\BlackBox\{ + PHPUnit\Framework\TestCase, PHPUnit\BlackBox, Set, Random, @@ -20,26 +20,20 @@ public function testInterface() { $set = MediaType::any(); - $this->assertInstanceOf(Set::class, $set); + $this->assertInstanceOf(Set\Provider::class, $set); - foreach ($set->values(Random::default) as $value) { + foreach ($set->toSet()->values(Random::default) as $value) { $this->assertInstanceOf(Set\Value::class, $value); - - if (\interface_exists(Set\Implementation::class)) { - $this->assertTrue($value->immutable()); - } else { - $this->assertTrue($value->isImmutable()); - } - + $this->assertTrue($value->immutable()); $this->assertInstanceOf(Model::class, $value->unwrap()); } } - public function testAllGeneratedMediaTypesAreParseable() + public function testAllGeneratedMediaTypesAreParseable(): BlackBox\Proof { - $this + return $this ->forAll(MediaType::any()) - ->then(function($mediaType) { + ->prove(function($mediaType) { $this->assertSame( $mediaType->toString(), Model::of($mediaType->toString())->toString(), diff --git a/tests/MediaTypeTest.php b/tests/MediaTypeTest.php index f34442a..191f12d 100644 --- a/tests/MediaTypeTest.php +++ b/tests/MediaTypeTest.php @@ -6,12 +6,11 @@ use Innmind\MediaType\{ MediaType, Parameter, - Exception\InvalidTopLevelType, - Exception\DomainException, + TopLevel, }; use Innmind\Immutable\Sequence; -use PHPUnit\Framework\TestCase; use Innmind\BlackBox\{ + PHPUnit\Framework\TestCase, PHPUnit\BlackBox, Set, }; @@ -22,15 +21,15 @@ class MediaTypeTest extends TestCase public function testInterface() { - $mediaType = new MediaType( - 'application', + $mediaType = MediaType::from( + TopLevel::application, 'json', 'whatever', - $parameter = new Parameter('charset', 'UTF-8'), + $parameter = Parameter::from('charset', 'UTF-8'), ); $this->assertTrue($mediaType->parameters()->equals(Sequence::of($parameter))); - $this->assertSame('application', $mediaType->topLevel()); + $this->assertSame(TopLevel::application, $mediaType->topLevel()); $this->assertSame('json', $mediaType->subType()); $this->assertSame('whatever', $mediaType->suffix()); $this->assertSame('application/json+whatever; charset=UTF-8', $mediaType->toString()); @@ -46,28 +45,13 @@ public function testStringCast() { $this->assertSame( 'application/json', - (new MediaType( - 'application', + MediaType::from( + TopLevel::application, 'json', - ))->toString(), + )->toString(), ); } - public function testThrowWhenTheTopLevelIsInvalid() - { - $this - ->forAll( - Set\Strings::any()->filter(static fn($string) => !MediaType::topLevels()->contains($string)), - Set\Strings::any(), - ) - ->then(function($topLevel, $subType) { - $this->expectException(InvalidTopLevelType::class); - $this->expectExceptionMessage($topLevel); - - new MediaType($topLevel, $subType); - }); - } - public function testMaybe() { $mediaType = MediaType::maybe( @@ -78,7 +62,7 @@ public function testMaybe() ); $this->assertInstanceOf(MediaType::class, $mediaType); - $this->assertSame('application', $mediaType->topLevel()); + $this->assertSame(TopLevel::application, $mediaType->topLevel()); $this->assertSame('tree.octet-stream', $mediaType->subType()); $this->assertSame('suffix', $mediaType->suffix()); $this->assertSame(3, $mediaType->parameters()->size()); @@ -105,7 +89,7 @@ public function testMaybeParametersInDoubleQuotes() ); $this->assertInstanceOf(MediaType::class, $mediaType); - $this->assertSame('application', $mediaType->topLevel()); + $this->assertSame(TopLevel::application, $mediaType->topLevel()); $this->assertSame('octet-stream', $mediaType->subType()); $this->assertSame(1, $mediaType->parameters()->size()); $parameters = $mediaType->parameters()->toList(); @@ -113,11 +97,11 @@ public function testMaybeParametersInDoubleQuotes() $this->assertSame('UTF-8', $parameters[0]->value()); } - public function testReturnNothingWhenInvalidMediaTypeString() + public function testReturnNothingWhenInvalidMediaTypeString(): BlackBox\Proof { - $this - ->forAll(Set\Strings::any()) - ->then(function($string) { + return $this + ->forAll(Set::strings()) + ->prove(function($string) { // this may optimistically generate a valid media type string at // some point but generally any random string is invalid $this->assertNull( @@ -139,31 +123,33 @@ public function testReturnNothingWhenTopLevelInvalid() ); } - public function testThrowWhenSubTypeInvalid() + public function testThrowWhenSubTypeInvalid(): BlackBox\Proof { - $this + return $this ->forAll( - Set\Strings::any()->filter(static fn($type) => !(bool) \preg_match('~^[A-Za-z0-9][A-Za-z0-9!#$&^_.-]{0,126}$~', $type)), + Set::strings()->exclude(static fn($type) => (bool) \preg_match('~^[A-Za-z0-9][A-Za-z0-9!#$&^_.-]{0,126}$~', $type)), ) - ->then(function($type) { - $this->expectException(DomainException::class); + ->prove(function($type) { + $this->expectException(\DomainException::class); $this->expectExceptionMessage($type); - new MediaType('application', $type); + MediaType::from(TopLevel::application, $type); }); } - public function testThrowWhenSuffixInvalid() + public function testThrowWhenSuffixInvalid(): BlackBox\Proof { - $this + return $this ->forAll( - Set\Strings::atLeast(1)->filter(static fn($suffix) => !(bool) \preg_match('~^[A-Za-z0-9][A-Za-z0-9!#$&^_.-]{0,126}$~', $suffix)), + Set::strings() + ->atLeast(1) + ->exclude(static fn($suffix) => (bool) \preg_match('~^[A-Za-z0-9][A-Za-z0-9!#$&^_.-]{0,126}$~', $suffix)), ) - ->then(function($suffix) { + ->prove(function($suffix) { try { - new MediaType('application', 'json', $suffix); + MediaType::from(TopLevel::application, 'json', $suffix); $this->fail('it should throw'); - } catch (DomainException $e) { + } catch (\DomainException $e) { $this->assertSame($suffix, $e->getMessage()); } }); diff --git a/tests/ParameterTest.php b/tests/ParameterTest.php index 054184e..382a62c 100644 --- a/tests/ParameterTest.php +++ b/tests/ParameterTest.php @@ -3,12 +3,9 @@ namespace Tests\Innmind\MediaType; -use Innmind\MediaType\{ - Parameter, - Exception\DomainException, -}; -use PHPUnit\Framework\TestCase; +use Innmind\MediaType\Parameter; use Innmind\BlackBox\{ + PHPUnit\Framework\TestCase, PHPUnit\BlackBox, Set, }; @@ -17,15 +14,15 @@ class ParameterTest extends TestCase { use BlackBox; - public function testInterface() + public function testInterface(): BlackBox\Proof { - $this + return $this ->forAll( - Set\Strings::any()->filter(static fn($name) => (bool) \preg_match('~^[A-Za-z0-9][A-Za-z0-9!#$&^_.-]{0,126}$~', $name)), - Set\Strings::any(), + Set::strings()->filter(static fn($name) => (bool) \preg_match('~^[A-Za-z0-9][A-Za-z0-9!#$&^_.-]{0,126}$~', $name)), + Set::strings(), ) - ->then(function($name, $value) { - $parameter = new Parameter($name, $value); + ->prove(function($name, $value) { + $parameter = Parameter::from($name, $value); $this->assertSame($name, $parameter->name()); $this->assertSame($value, $parameter->value()); @@ -33,39 +30,41 @@ public function testInterface() }); } - public function testThrowWhenNameInvalid() + public function testThrowWhenNameInvalid(): BlackBox\Proof { - $this + return $this ->forAll( - Set\Strings::any()->filter(static fn($name) => !(bool) \preg_match('~^[\w\-.]+$~', $name)), - Set\Strings::any(), + Set::strings()->exclude(static fn($name) => (bool) \preg_match('~^[\w\-.]+$~', $name)), + Set::strings(), ) - ->then(function($name, $value) { - $this->expectException(DomainException::class); + ->prove(function($name, $value) { + $this->expectException(\DomainException::class); $this->expectExceptionMessage($name); - new Parameter($name, $value); + Parameter::from($name, $value); }); } - public function testAcceptValueContainedInDoubleQuotes() + public function testAcceptValueContainedInDoubleQuotes(): BlackBox\Proof { - $this + return $this ->forAll( - Set\Composite::immutable( + Set::compose( static fn($first, $rest) => $first.$rest, - Set\Chars::alphanumerical(), - Set\Strings::madeOf( - Set\Chars::alphanumerical(), - Set\Elements::of('!', '#', '$', '&', '^', '_', '.', '-'), - )->between(0, 125), + Set::strings()->chars()->alphanumerical(), + Set::strings() + ->madeOf( + Set\Chars::alphanumerical(), + Set::of('!', '#', '$', '&', '^', '_', '.', '-'), + ) + ->between(0, 125), ), - Set\Strings::madeOf( - Set\Chars::alphanumerical(), - Set\Elements::of('!', '#', '$', '&', '^', '_', '.', '-', "'", '*', '+', '`', '|', '~'), + Set::strings()->madeOf( + Set::strings()->chars()->alphanumerical(), + Set::of('!', '#', '$', '&', '^', '_', '.', '-', "'", '*', '+', '`', '|', '~'), ), ) - ->then(function($name, $value) { + ->prove(function($name, $value) { $parameter = Parameter::of(\sprintf( '%s="%s"', $name,