From f8468489d8529249b6dc221163d42d860af06d8e Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Thu, 14 May 2026 17:24:56 +0200 Subject: [PATCH 01/11] use blackbox 7 --- CHANGELOG.md | 6 ++ blackbox.php | 3 +- composer.json | 6 +- fixtures/File.php | 2 +- proofs/adapter/filesystem.php | 68 ++++++++++--------- proofs/adapter/inMemory.php | 18 ++--- proofs/directory/directory.php | 62 ++++++++++------- proofs/file.php | 15 ++-- proofs/file/content.php | 66 +++++++++--------- properties/Adapter.php | 6 +- .../AddDirectoryFromAnotherAdapter.php | 2 +- ...hSameNameAsDirectoryDeleteTheDirectory.php | 2 +- ...dRemoveAddModificationsStillAddTheFile.php | 2 +- ...AddRemoveModificationsDoesntAddTheFile.php | 2 +- properties/Adapter/RemoveFileInDirectory.php | 2 +- properties/Content.php | 8 +-- properties/Directory.php | 6 +- properties/Directory/FlatMapFiles.php | 2 +- .../ThrowWhenFlatMappingToSameFileTwice.php | 2 +- tests/Adapter/FilesystemTest.php | 36 +++++----- tests/DirectoryTest.php | 44 ++++++------ tests/NameTest.php | 38 +++++++---- 22 files changed, 218 insertions(+), 180 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 799b840..1773d77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [Unreleased] + +### Changed + +- Requires `innmind/black-box:~7.0` + ## 9.0.0 - 2026-01-25 ### Added diff --git a/blackbox.php b/blackbox.php index 6195716..d7a230e 100644 --- a/blackbox.php +++ b/blackbox.php @@ -25,8 +25,7 @@ __DIR__.'/proofs/', __DIR__.'/fixtures/', ) - ->dumpTo('coverage.clover') - ->enableWhen(true), + ->dumpTo('coverage.clover'), ) ->scenariiPerProof(1), ) diff --git a/composer.json b/composer.json index 8100d45..204c277 100644 --- a/composer.json +++ b/composer.json @@ -37,18 +37,18 @@ }, "require-dev": { "innmind/static-analysis": "~1.3", - "innmind/black-box": "~6.5", + "innmind/black-box": "~7.0", "innmind/coding-standard": "~2.0", "symfony/filesystem": "~6.0|~7.0|~8.0", "ramsey/uuid": "^4.6" }, "conflict": { - "innmind/black-box": "<6.0|~7.0" + "innmind/black-box": "<7.0|~8.0" }, "suggest": { "innmind/black-box": "For property based testing" }, "provide": { - "innmind/black-box-sets": "6.0" + "innmind/black-box-sets": "7.0" } } diff --git a/fixtures/File.php b/fixtures/File.php index 5300a43..dad4f53 100644 --- a/fixtures/File.php +++ b/fixtures/File.php @@ -23,6 +23,6 @@ public static function any(): Set Name::any(), Set::strings(), MediaType::any(), - )->toSet(); + ); } } diff --git a/proofs/adapter/filesystem.php b/proofs/adapter/filesystem.php index b6c4c47..e18007a 100644 --- a/proofs/adapter/filesystem.php +++ b/proofs/adapter/filesystem.php @@ -18,13 +18,13 @@ use Innmind\BlackBox\Set; use Symfony\Component\Filesystem\Filesystem as FS; -return static function() { +return static function($prove) { $path = \rtrim(\sys_get_temp_dir(), '/').'/innmind/filesystem/'; - yield properties( + yield $prove->properties( 'Filesystem properties', PAdapter::properties(), - Set::call(static function() use ($path) { + Set::of(static function() use ($path) { (new FS)->remove($path); return Adapter::mount( @@ -40,25 +40,27 @@ ); foreach (PAdapter::alwaysApplicable() as $property) { - yield property( - $property, - Set::call(static function() use ($path) { - (new FS)->remove($path); + yield $prove + ->property( + $property, + Set::of(static function() use ($path) { + (new FS)->remove($path); - return Adapter::mount( - Path::of($path), - match (\PHP_OS) { - 'Darwin' => CaseSensitivity::insensitive, - default => CaseSensitivity::sensitive, - }, - ) - ->recover(Recover::mount(...)) - ->unwrap(); - }), - )->named('Filesystem'); + return Adapter::mount( + Path::of($path), + match (\PHP_OS) { + 'Darwin' => CaseSensitivity::insensitive, + default => CaseSensitivity::sensitive, + }, + ) + ->recover(Recover::mount(...)) + ->unwrap(); + }), + ) + ->named('Filesystem'); } - yield test( + yield $prove->test( 'Regression adding file in directory due to case sensitivity', static function($assert) use ($path) { $property = new PAdapter\AddRemoveAddModificationsStillAddTheFile( @@ -86,10 +88,10 @@ static function($assert) use ($path) { ); foreach (CaseSensitivity::cases() as $case) { - yield properties( + yield $prove->properties( 'Filesystem properties on simulated disk', PAdapter::properties(), - Set::call(static fn() => Adapter::mount( + Set::of(static fn() => Adapter::mount( Path::of('/'), $case, IO::simulation( @@ -100,17 +102,19 @@ static function($assert) use ($path) { ); foreach (PAdapter::alwaysApplicable() as $property) { - yield property( - $property, - Set::call(static fn() => Adapter::mount( - Path::of('/'), - $case, - IO::simulation( - IO::fromAmbientAuthority(), - Disk::new(), - ), - )->unwrap()), - )->named('Filesystem on simulated disk'); + yield $prove + ->property( + $property, + Set::of(static fn() => Adapter::mount( + Path::of('/'), + $case, + IO::simulation( + IO::fromAmbientAuthority(), + Disk::new(), + ), + )->unwrap()), + ) + ->named('Filesystem on simulated disk'); } } }; diff --git a/proofs/adapter/inMemory.php b/proofs/adapter/inMemory.php index 7ba4045..6545ff5 100644 --- a/proofs/adapter/inMemory.php +++ b/proofs/adapter/inMemory.php @@ -10,21 +10,23 @@ use Properties\Innmind\Filesystem\Adapter as PAdapter; use Innmind\BlackBox\Set; -return static function() { - yield properties( +return static function($prove) { + yield $prove->properties( 'InMemory properties emulating filesystem', PAdapter::properties(), - Set::call(Adapter::inMemory(...)), + Set::of(Adapter::inMemory(...)), ); foreach (PAdapter::alwaysApplicable() as $property) { - yield property( - $property, - Set::call(Adapter::inMemory(...)), - )->named('InMemory emulating filesystem'); + yield $prove + ->property( + $property, + Set::of(Adapter::inMemory(...)), + ) + ->named('InMemory emulating filesystem'); } - yield test( + yield $prove->test( 'Adding a file in a directory should not remove other files starting with the same name', static function($assert) { $adapter = Adapter::inMemory(); diff --git a/proofs/directory/directory.php b/proofs/directory/directory.php index 213adae..16f418e 100644 --- a/proofs/directory/directory.php +++ b/proofs/directory/directory.php @@ -10,13 +10,16 @@ use Properties\Innmind\Filesystem\Directory as PDirectory; use Innmind\BlackBox\Set; -return static function() { - yield properties( +return static function($prove) { + yield $prove->properties( 'Empty Directory properties', PDirectory::properties(), - Name::any()->map(Directory::of(...)), + Name::any() + ->map(Directory::of(...)) + ->map(static fn($directory) => static fn() => $directory), ); - yield properties( + + yield $prove->properties( 'Non empty Directory properties', PDirectory::properties(), Set::compose( @@ -32,30 +35,37 @@ ->groupBy(static fn($file) => $file->name()->toString()) ->size() === $files->size(), // do not accept duplicated files ), - ), + )->map(static fn($directory) => static fn() => $directory), ); foreach (PDirectory::alwaysApplicable() as $property) { - yield property( - $property, - Name::any()->map(Directory::of(...)), - )->named('Empty Directory'); - yield property( - $property, - Set::compose( - Directory::of(...), - Name::any(), - Sequence::of( - File::any()->randomize(), - Set::integers() - ->between(1, 5) // only to speed up tests - ->toSet(), - )->filter( - static fn($files) => $files - ->groupBy(static fn($file) => $file->name()->toString()) - ->size() === $files->size(), // do not accept duplicated files - ), - ), - )->named('Non empty Directory'); + yield $prove + ->property( + $property, + Name::any() + ->map(Directory::of(...)) + ->map(static fn($directory) => static fn() => $directory), + ) + ->named('Empty Directory'); + + yield $prove + ->property( + $property, + Set::compose( + Directory::of(...), + Name::any(), + Sequence::of( + File::any()->randomize(), + Set::integers() + ->between(1, 5) // only to speed up tests + ->toSet(), + )->filter( + static fn($files) => $files + ->groupBy(static fn($file) => $file->name()->toString()) + ->size() === $files->size(), // do not accept duplicated files + ), + )->map(static fn($directory) => static fn() => $directory), + ) + ->named('Non empty Directory'); } }; diff --git a/proofs/file.php b/proofs/file.php index e6de683..2211d1f 100644 --- a/proofs/file.php +++ b/proofs/file.php @@ -3,14 +3,14 @@ use Fixtures\Innmind\Filesystem\File; -return static function() { - yield proof( - 'File::mapContent()', - given( +return static function($prove) { + yield $prove + ->proof('File::mapContent()') + ->given( File::any(), File::any(), - ), - static function($assert, $file, $replacement) { + ) + ->test(static function($assert, $file, $replacement) { $new = $file->mapContent(static function($content) use ($assert, $file, $replacement) { $assert->same($file->content(), $content); @@ -24,6 +24,5 @@ static function($assert, $file, $replacement) { $assert->same($file->name(), $new->name()); $assert->same($file->mediaType(), $new->mediaType()); $assert->same($replacement->content(), $new->content()); - }, - ); + }); }; diff --git a/proofs/file/content.php b/proofs/file/content.php index 734c346..b1817b6 100644 --- a/proofs/file/content.php +++ b/proofs/file/content.php @@ -16,7 +16,7 @@ Monoid\Concat, }; -return static function() { +return static function($prove) { $io = IO::fromAmbientAuthority(); $implementations = [ @@ -73,21 +73,23 @@ ]; foreach ($implementations as [$name, $content]) { - yield properties( + yield $prove->properties( $name, Content::properties(), - $content, + $content->map(static fn($content) => static fn() => $content), ); foreach (Content::all() as $property) { - yield property( - $property, - $content, - )->named($name); + yield $prove + ->property( + $property, + $content->map(static fn($content) => static fn() => $content), + ) + ->named($name); } } - yield test( + yield $prove->test( 'Content::oneShot()->foreach()', static function($assert) use ($io) { $content = Model::oneShot($io->streams()->acquire( @@ -103,9 +105,9 @@ static function($assert) use ($io) { }, ); - yield proof( - 'Content::oneShot()->map()', - given( + yield $prove + ->proof('Content::oneShot()->map()') + ->given( Set::strings() ->madeOf( Set::strings() @@ -115,8 +117,8 @@ static function($assert) use ($io) { ) ->map(Str::of(...)) ->map(Line::of(...)), - ), - static function($assert, $replacement) use ($io) { + ) + ->test(static function($assert, $replacement) use ($io) { $content = Model::oneShot($io->streams()->acquire( \fopen('LICENSE', 'r'), )); @@ -129,12 +131,11 @@ static function($assert, $replacement) use ($io) { ->toList(); $assert->same([$replacement->toString()], $lines); - }, - ); + }); - yield proof( - 'Content::oneShot()->flatMap()', - given( + yield $prove + ->proof('Content::oneShot()->flatMap()') + ->given( Set::strings()->madeOf( Set::strings() ->unicode() @@ -147,8 +148,9 @@ static function($assert, $replacement) use ($io) { ->char() ->filter(static fn($char) => $char !== "\n"), ), - )->filter(static fn($a, $b) => $a !== $b), - static function($assert, $replacement1, $replacement2) use ($io) { + ) + ->filter(static fn($a, $b) => $a !== $b) + ->test(static function($assert, $replacement1, $replacement2) use ($io) { $content = Model::oneShot($io->streams()->acquire( \fopen('LICENSE', 'r'), )); @@ -161,10 +163,9 @@ static function($assert, $replacement1, $replacement2) use ($io) { ->toList(); $assert->same([$replacement1, $replacement2], $lines); - }, - ); + }); - yield test( + yield $prove->test( 'Content::oneShot()->filter()', static function($assert) use ($io) { $content = Model::oneShot($io->streams()->acquire( @@ -180,7 +181,7 @@ static function($assert) use ($io) { }, ); - yield test( + yield $prove->test( 'Content::oneShot()->reduce()', static function($assert) use ($io) { $content = Model::oneShot($io->streams()->acquire( @@ -196,7 +197,7 @@ static function($assert) use ($io) { }, ); - yield test( + yield $prove->test( 'Content::oneShot()->toString()', static function($assert) use ($io) { $content = Model::oneShot($io->streams()->acquire( @@ -207,7 +208,7 @@ static function($assert) use ($io) { }, ); - yield test( + yield $prove->test( 'Content::oneShot()->chunks()', static function($assert) use ($io) { $content = Model::oneShot($io->streams()->acquire( @@ -237,20 +238,19 @@ static function($assert) use ($io) { ->toString(), ); - yield proof( - 'Content::oneShot() throws when loaded multiple times', - given($actions, $actions), - static function($assert, $a, $b) use ($io) { + yield $prove + ->proof('Content::oneShot() throws when loaded multiple times') + ->given($actions, $actions) + ->test(static function($assert, $a, $b) use ($io) { $content = Model::oneShot($io->streams()->acquire( \fopen('LICENSE', 'r'), )); $a($content); $assert->throws(static fn() => $b($content)); - }, - ); + }); - yield test( + yield $prove->test( 'Content::ofChunks()->size() does not load the whole file in memory', static function($assert) use ($io) { $atPath = $io diff --git a/properties/Adapter.php b/properties/Adapter.php index c287282..b520ffe 100644 --- a/properties/Adapter.php +++ b/properties/Adapter.php @@ -11,11 +11,11 @@ final class Adapter { /** - * @return Set\Provider|Set + * @return Set */ - public static function properties(): Set\Provider|Set + public static function properties(): Set { - return Set\Properties::any(...self::list())->atMost(50); + return Set::properties(...self::list())->atMost(50); } /** diff --git a/properties/Adapter/AddDirectoryFromAnotherAdapter.php b/properties/Adapter/AddDirectoryFromAnotherAdapter.php index 3c2727a..61a40ad 100644 --- a/properties/Adapter/AddDirectoryFromAnotherAdapter.php +++ b/properties/Adapter/AddDirectoryFromAnotherAdapter.php @@ -37,7 +37,7 @@ public function __construct(Name $name, File $file) $this->file = $file; } - public static function any(): Set\Provider + public static function any(): Set { return Set::compose( static fn(...$args) => new self(...$args), diff --git a/properties/Adapter/AddFileWithSameNameAsDirectoryDeleteTheDirectory.php b/properties/Adapter/AddFileWithSameNameAsDirectoryDeleteTheDirectory.php index 91ffa89..b6befd7 100644 --- a/properties/Adapter/AddFileWithSameNameAsDirectoryDeleteTheDirectory.php +++ b/properties/Adapter/AddFileWithSameNameAsDirectoryDeleteTheDirectory.php @@ -35,7 +35,7 @@ public function __construct(File $file, File $fileInDirectory) $this->directory = Directory::of($file->name())->add($fileInDirectory); } - public static function any(): Set\Provider + public static function any(): Set { return Set::compose( static fn(...$args) => new self(...$args), diff --git a/properties/Adapter/AddRemoveAddModificationsStillAddTheFile.php b/properties/Adapter/AddRemoveAddModificationsStillAddTheFile.php index 2a93d1f..46c94d1 100644 --- a/properties/Adapter/AddRemoveAddModificationsStillAddTheFile.php +++ b/properties/Adapter/AddRemoveAddModificationsStillAddTheFile.php @@ -32,7 +32,7 @@ public function __construct(Directory $directory, File $file) $this->file = $file; } - public static function any(): Set\Provider + public static function any(): Set { return Set::compose( static fn(...$args) => new self(...$args), diff --git a/properties/Adapter/RemoveAddRemoveModificationsDoesntAddTheFile.php b/properties/Adapter/RemoveAddRemoveModificationsDoesntAddTheFile.php index f7b0603..a68416a 100644 --- a/properties/Adapter/RemoveAddRemoveModificationsDoesntAddTheFile.php +++ b/properties/Adapter/RemoveAddRemoveModificationsDoesntAddTheFile.php @@ -32,7 +32,7 @@ public function __construct(Directory $directory, File $file) $this->file = $file; } - public static function any(): Set\Provider + public static function any(): Set { return Set::compose( static fn(...$args) => new self(...$args), diff --git a/properties/Adapter/RemoveFileInDirectory.php b/properties/Adapter/RemoveFileInDirectory.php index 310ac0e..09dcd85 100644 --- a/properties/Adapter/RemoveFileInDirectory.php +++ b/properties/Adapter/RemoveFileInDirectory.php @@ -34,7 +34,7 @@ public function __construct(File $file, Name $name) $this->name = $name; } - public static function any(): Set\Provider + public static function any(): Set { return Set::compose( static fn(...$args) => new self(...$args), diff --git a/properties/Content.php b/properties/Content.php index c1933a1..1d1be7b 100644 --- a/properties/Content.php +++ b/properties/Content.php @@ -13,16 +13,16 @@ final class Content { /** - * @return Set\Provider|Set + * @return Set */ - public static function properties(): Set\Provider|Set + public static function properties(): Set { - return Set\Properties::any( + return Set::properties( ...\array_map( static fn($class) => $class::any(), self::all(), ), - ); + )->toSet(); } /** diff --git a/properties/Directory.php b/properties/Directory.php index 4704c74..80d86bf 100644 --- a/properties/Directory.php +++ b/properties/Directory.php @@ -11,11 +11,11 @@ final class Directory { /** - * @return Set\Provider|Set + * @return Set */ - public static function properties(): Set\Provider|Set + public static function properties(): Set { - return Set\Properties::any(...self::list())->atMost(50); + return Set::properties(...self::list())->atMost(50); } /** diff --git a/properties/Directory/FlatMapFiles.php b/properties/Directory/FlatMapFiles.php index 2f084d3..fcc7997 100644 --- a/properties/Directory/FlatMapFiles.php +++ b/properties/Directory/FlatMapFiles.php @@ -31,7 +31,7 @@ public function __construct(File $file1, File $file2) $this->file2 = $file2; } - public static function any(): Set\Provider + public static function any(): Set { return Set::compose( static fn(...$args) => new self(...$args), diff --git a/properties/Directory/ThrowWhenFlatMappingToSameFileTwice.php b/properties/Directory/ThrowWhenFlatMappingToSameFileTwice.php index 4805bee..ca48729 100644 --- a/properties/Directory/ThrowWhenFlatMappingToSameFileTwice.php +++ b/properties/Directory/ThrowWhenFlatMappingToSameFileTwice.php @@ -30,7 +30,7 @@ public function __construct(File $file1, File $file2) $this->file2 = $file2; } - public static function any(): Set\Provider + public static function any(): Set { return Set::compose( static fn(...$args) => new self(...$args), diff --git a/tests/Adapter/FilesystemTest.php b/tests/Adapter/FilesystemTest.php index 3417294..5d0013a 100644 --- a/tests/Adapter/FilesystemTest.php +++ b/tests/Adapter/FilesystemTest.php @@ -61,10 +61,12 @@ public function testInterface() public function testThrowWhenPathToMountIsNotADirectory() { - $this->expectException(\LogicException::class); - $this->expectExceptionMessage("Path doesn't represent a directory 'path/to/somewhere'"); - - $_ = Adapter::mount(Path::of('path/to/somewhere'))->unwrap(); + $this + ->assert() + ->throws( + static fn() => Adapter::mount(Path::of('path/to/somewhere'))->unwrap(), + \LogicException::class, + ); } public function testReturnNothingWhenGettingUnknownFile() @@ -331,28 +333,30 @@ public function testPathTooLongThrowAnException() ->recover(Recover::mount(...)) ->unwrap(); - $this->expectException(\RuntimeException::class); - $this->expectExceptionMessage('Path too long'); - - $_ = $filesystem->add(Directory::of( - Name::of(\str_repeat('a', 255)), - Sequence::of( - Directory::of( + $this + ->assert() + ->throws( + static fn() => $filesystem->add(Directory::of( Name::of(\str_repeat('a', 255)), Sequence::of( Directory::of( Name::of(\str_repeat('a', 255)), Sequence::of( - File::of( + Directory::of( Name::of(\str_repeat('a', 255)), - Content::none(), + Sequence::of( + File::of( + Name::of(\str_repeat('a', 255)), + Content::none(), + ), + ), ), ), ), ), - ), - ), - ))->unwrap(); + ))->unwrap(), + \RuntimeException::class, + ); } public function testPersistedNameCanStartWithAnyAsciiCharacter(): BlackBox\Proof diff --git a/tests/DirectoryTest.php b/tests/DirectoryTest.php index 3f0c47e..cb5b846 100644 --- a/tests/DirectoryTest.php +++ b/tests/DirectoryTest.php @@ -194,16 +194,18 @@ public function testDirectoryLoadedWithDifferentFilesWithTheSameNameThrows(): Bl FName::any(), ) ->prove(function($directory, $file) { - $this->expectException(\LogicException::class); - $this->expectExceptionMessage("Same file '{$file->toString()}' found multiple times"); - - $_ = Directory::of( - $directory, - Sequence::of( - File::named($file->toString(), Content::none()), - File::named($file->toString(), Content::none()), - ), - ); + $this + ->assert() + ->throws( + static fn() => Directory::of( + $directory, + Sequence::of( + File::named($file->toString(), Content::none()), + File::named($file->toString(), Content::none()), + ), + ), + \LogicException::class, + ); }); } @@ -215,16 +217,18 @@ public function testNamedDirectoryLoadedWithDifferentFilesWithTheSameNameThrows( FName::any(), ) ->prove(function($directory, $file) { - $this->expectException(\LogicException::class); - $this->expectExceptionMessage("Same file '{$file->toString()}' found multiple times"); - - $_ = Directory::named( - $directory->toString(), - Sequence::of( - File::named($file->toString(), Content::none()), - File::named($file->toString(), Content::none()), - ), - ); + $this + ->assert() + ->throws( + static fn() => Directory::named( + $directory->toString(), + Sequence::of( + File::named($file->toString(), Content::none()), + File::named($file->toString(), Content::none()), + ), + ), + \LogicException::class, + ); }); } diff --git a/tests/NameTest.php b/tests/NameTest.php index 7a97847..5e2bae2 100644 --- a/tests/NameTest.php +++ b/tests/NameTest.php @@ -26,10 +26,12 @@ public function testInterface() public function testThrowWhenABuildingNameWithASlash() { - $this->expectException(\DomainException::class); - $this->expectExceptionMessage('A file name can\'t contain a slash, foo/bar given'); - - $_ = Name::of('foo/bar'); + $this + ->assert() + ->throws( + static fn() => Name::of('foo/bar'), + \DomainException::class, + ); } public function testEquals() @@ -40,10 +42,12 @@ public function testEquals() public function testEmptyNameIsNotAllowed() { - $this->expectException(\DomainException::class); - $this->expectExceptionMessage('A file name can\'t be empty'); - - $_ = Name::of(''); + $this + ->assert() + ->throws( + static fn() => Name::of(''), + \DomainException::class, + ); } public function testAcceptsAnyValueNotContainingASlash(): BlackBox\Proof @@ -67,9 +71,12 @@ public function testNameContainingASlashIsNotAccepted(): BlackBox\Proof Fixture::strings(), ) ->prove(function($a, $b) { - $this->expectException(\DomainException::class); - - $_ = Name::of("$a/$b"); + $this + ->assert() + ->throws( + static fn() => Name::of("$a/$b"), + \DomainException::class, + ); }); } @@ -121,9 +128,12 @@ public function testDotFoldersAreNotAccepted(): BlackBox\Proof public function testChr0IsNotAccepted() { - $this->expectException(\DomainException::class); - - $_ = Name::of('a'.\chr(0).'a'); + $this + ->assert() + ->throws( + static fn() => Name::of('a'.\chr(0).'a'), + \DomainException::class, + ); } public function testNamesLongerThan255AreNotAccepted(): BlackBox\Proof From c36ffa9a7381f0a98665e356735546684c0d0318 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Thu, 14 May 2026 17:29:15 +0200 Subject: [PATCH 02/11] requires php 8.5 --- .github/workflows/ci.yml | 39 ++++++++++++++++++++++++++++++++++++--- CHANGELOG.md | 1 + composer.json | 2 +- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bfa5286..8e94d93 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,13 +4,46 @@ on: [push] jobs: blackbox: - uses: innmind/github-workflows/.github/workflows/black-box-matrix.yml@main + uses: innmind/github-workflows/.github/workflows/black-box.yml@main + name: 'BlackBox' + strategy: + matrix: + os: [ubuntu-latest, macOS-latest] + php-version: ['8.5'] + dependency-versions: ['lowest', 'highest'] with: scenarii: 20 + os: ${{ matrix.os }} + php-version: ${{ matrix.php-version }} + dependency-versions: ${{ matrix.dependency-versions }} coverage: - uses: innmind/github-workflows/.github/workflows/coverage-matrix.yml@main + uses: innmind/github-workflows/.github/workflows/coverage.yml@main + name: 'Coverage' secrets: inherit + strategy: + matrix: + os: [ubuntu-latest, macOS-latest] + php-version: ['8.5'] + dependency-versions: ['lowest', 'highest'] + with: + os: ${{ matrix.os }} + php-version: ${{ matrix.php-version }} + dependency-versions: ${{ matrix.dependency-versions }} psalm: - uses: innmind/github-workflows/.github/workflows/psalm-matrix.yml@main + uses: innmind/github-workflows/.github/workflows/psalm.yml@main + name: 'Psalm' + strategy: + matrix: + php-version: ['8.5'] + run-php-version: ['8.5'] + with: + php-version: ${{ matrix.php-version }} + run-php-version: ${{ matrix.run-php-version }} cs: uses: innmind/github-workflows/.github/workflows/cs.yml@main + name: 'CS' + strategy: + matrix: + php-version: ['8.5'] + with: + php-version: ${{ matrix.php-version }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 1773d77..c03e01a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Changed +- Requires PHP `8.5` - Requires `innmind/black-box:~7.0` ## 9.0.0 - 2026-01-25 diff --git a/composer.json b/composer.json index 204c277..aaa59f4 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "issues": "http://github.com/Innmind/filesystem/issues" }, "require": { - "php": "~8.4", + "php": "~8.5", "innmind/immutable": "~6.0", "innmind/media-type": "~3.0", "innmind/url": "~5.0", From 548e62722134fa474d377a7b6adb7014f44ad0c7 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Fri, 3 Jul 2026 16:49:55 +0200 Subject: [PATCH 03/11] prevent using versions of innmind/url that generate invalid urls --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index aaa59f4..7717050 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "php": "~8.5", "innmind/immutable": "~6.0", "innmind/media-type": "~3.0", - "innmind/url": "~5.0", + "innmind/url": "^5.3.2", "psr/log": "~3.0", "innmind/io": "~4.0", "innmind/validation": "~3.0" From 763952d57f59bee4b2d1690840981231cc651d72 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sat, 1 Aug 2026 15:16:38 +0200 Subject: [PATCH 04/11] make sure all paths are treated as file paths --- composer.json | 2 +- src/Adapter.php | 2 +- src/Adapter/InMemory.php | 2 +- src/Adapter/TreePath.php | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index 7717050..a300a99 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "php": "~8.5", "innmind/immutable": "~6.0", "innmind/media-type": "~3.0", - "innmind/url": "^5.3.2", + "innmind/url": "~5.4", "psr/log": "~3.0", "innmind/io": "~4.0", "innmind/validation": "~3.0" diff --git a/src/Adapter.php b/src/Adapter.php index cc1045d..6151ad2 100644 --- a/src/Adapter.php +++ b/src/Adapter.php @@ -48,7 +48,7 @@ public static function mount( CaseSensitivity $case = CaseSensitivity::sensitive, ?IO $io = null, ): Attempt { - return Filesystem::mount($path, $io) + return Filesystem::mount(Path::file($path->toString()), $io) ->map(static fn($implementation) => new self( $implementation, $case, diff --git a/src/Adapter/InMemory.php b/src/Adapter/InMemory.php index 04d9895..418570a 100644 --- a/src/Adapter/InMemory.php +++ b/src/Adapter/InMemory.php @@ -169,6 +169,6 @@ public function write(TreePath $parent, File $file): Attempt private function path(TreePath $path): string { - return $path->asPath(Path::of('/'))->toString(); + return $path->asPath(Path::file('/'))->toString(); } } diff --git a/src/Adapter/TreePath.php b/src/Adapter/TreePath.php index 8660ebb..29da024 100644 --- a/src/Adapter/TreePath.php +++ b/src/Adapter/TreePath.php @@ -75,7 +75,7 @@ public function under(self $parent): self public function equals(self $other): bool { - $root = Path::of('/'); + $root = Path::file('/'); return $this->asPath($root)->equals($other->asPath($root)); } @@ -96,6 +96,6 @@ public function asPath(Path $root): Path $path = $path->dropEnd(1); // remove trailing '/' } - return $root->resolve(Path::of($path->toString())); + return $root->resolve(Path::file($path->toString())); } } From ac6c014b130cb974122710e03fd075ebf03ba2ac Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sat, 1 Aug 2026 16:26:57 +0200 Subject: [PATCH 05/11] use the ::file constructor as what is used internally --- tests/NameTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/NameTest.php b/tests/NameTest.php index 5e2bae2..67511ae 100644 --- a/tests/NameTest.php +++ b/tests/NameTest.php @@ -195,7 +195,7 @@ public function testAnySequenceOfNamesConstitutesAValidPath(): BlackBox\Proof $this->assertInstanceOf( Path::class, - Path::of($path), + Path::file($path), ); }); } From 466947da1d3cfc0bfbe3dd2ca3305c2faf5e6d98 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sat, 1 Aug 2026 16:27:13 +0200 Subject: [PATCH 06/11] fix innmind/io handling any file path --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index a300a99..7da1f88 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "innmind/media-type": "~3.0", "innmind/url": "~5.4", "psr/log": "~3.0", - "innmind/io": "~4.0", + "innmind/io": "^4.1.2", "innmind/validation": "~3.0" }, "autoload": { From 63f2ca94156af40b55870c346e1a5d067bc69634 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sat, 1 Aug 2026 16:27:31 +0200 Subject: [PATCH 07/11] do not generated backslashes as it often creates parsing errors --- fixtures/Name.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/fixtures/Name.php b/fixtures/Name.php index 21c44c9..beaa6f7 100644 --- a/fixtures/Name.php +++ b/fixtures/Name.php @@ -44,11 +44,14 @@ public function prefixedBy(string $prefix): self #[\Override] public function toSet(): Set { - return Set::strings()->madeOf( - Set::integers()->between(32, 46)->map(\chr(...)), - Set::integers()->between(48, 126)->map(\chr(...)), - Set::strings()->unicode()->emoticons(), - ) + return Set::strings() + ->madeOf( + Set::integers()->between(32, 46)->map(\chr(...)), + Set::integers()->between(48, 91)->map(\chr(...)), + // the backslash is excluded as it oftens creates parsing errors in Path::file() + Set::integers()->between(93, 126)->map(\chr(...)), + Set::strings()->unicode()->emoticons(), + ) ->between(1, 255 - \mb_strlen($this->prefix, 'ASCII')) ->map(fn($name) => $this->prefix.$name) ->filter(static fn($name) => \mb_strlen($name, 'ASCII') <= 255) From 9e8d1db7ddb99ae8773589c7daa113f7f8883937 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sat, 1 Aug 2026 16:31:54 +0200 Subject: [PATCH 08/11] exclude generating a backslash --- tests/Adapter/FilesystemTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/Adapter/FilesystemTest.php b/tests/Adapter/FilesystemTest.php index 5d0013a..861bf65 100644 --- a/tests/Adapter/FilesystemTest.php +++ b/tests/Adapter/FilesystemTest.php @@ -437,7 +437,9 @@ public function testPersistedNameCanContainOnlyOneAsciiCharacter(): BlackBox\Pro Set::integers()->between(1, 8), Set::integers()->between(14, 31), Set::integers()->between(33, 45), - Set::integers()->between(48, 127), + Set::integers()->between(48, 91), + // the backslash is excluded as it oftens creates parsing errors in Path::file() + Set::integers()->between(93, 126), ), Set::strings(), ) From eb6d216c999952a94c823b81a428f5898114f4dc Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sat, 1 Aug 2026 16:32:39 +0200 Subject: [PATCH 09/11] simplify ci --- .github/workflows/ci.yml | 42 +++------------------------------------- 1 file changed, 3 insertions(+), 39 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e94d93..5e4271b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,46 +4,10 @@ on: [push] jobs: blackbox: - uses: innmind/github-workflows/.github/workflows/black-box.yml@main - name: 'BlackBox' - strategy: - matrix: - os: [ubuntu-latest, macOS-latest] - php-version: ['8.5'] - dependency-versions: ['lowest', 'highest'] - with: - scenarii: 20 - os: ${{ matrix.os }} - php-version: ${{ matrix.php-version }} - dependency-versions: ${{ matrix.dependency-versions }} + uses: innmind/github-workflows/.github/workflows/black-box-matrix.yml@main coverage: - uses: innmind/github-workflows/.github/workflows/coverage.yml@main - name: 'Coverage' - secrets: inherit - strategy: - matrix: - os: [ubuntu-latest, macOS-latest] - php-version: ['8.5'] - dependency-versions: ['lowest', 'highest'] - with: - os: ${{ matrix.os }} - php-version: ${{ matrix.php-version }} - dependency-versions: ${{ matrix.dependency-versions }} + uses: innmind/github-workflows/.github/workflows/coverage-matrix.yml@main psalm: - uses: innmind/github-workflows/.github/workflows/psalm.yml@main - name: 'Psalm' - strategy: - matrix: - php-version: ['8.5'] - run-php-version: ['8.5'] - with: - php-version: ${{ matrix.php-version }} - run-php-version: ${{ matrix.run-php-version }} + uses: innmind/github-workflows/.github/workflows/psalm-matrix.yml@main cs: uses: innmind/github-workflows/.github/workflows/cs.yml@main - name: 'CS' - strategy: - matrix: - php-version: ['8.5'] - with: - php-version: ${{ matrix.php-version }} From 11aca098628cfc75b235771d41f3a6cdd74acc3e Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sat, 1 Aug 2026 16:33:06 +0200 Subject: [PATCH 10/11] run the ci on pull requests --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5e4271b..efedbe6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,6 @@ name: CI -on: [push] +on: [push, pull_request] jobs: blackbox: From 76fd9ea75db9f64022e6427100ff3d3f05361d56 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sat, 1 Aug 2026 16:34:14 +0200 Subject: [PATCH 11/11] use blackbox env --- blackbox.php | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/blackbox.php b/blackbox.php index d7a230e..792eb7a 100644 --- a/blackbox.php +++ b/blackbox.php @@ -11,14 +11,9 @@ Application::new($argv) ->disableMemoryLimit() // because the generated trees can be quite large - ->scenariiPerProof(20) - ->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 + ->map(static fn($app) => match (\getenv('BLACKBOX_ENV')) { + 'extensive' => $app->scenariiPerProof(1_000), + 'coverage' => $app ->codeCoverage( CodeCoverage::of( __DIR__.'/src/', @@ -28,7 +23,8 @@ ->dumpTo('coverage.clover'), ) ->scenariiPerProof(1), - ) + default => $app->scenariiPerProof(20), + }) ->allowProofsToNotMakeAnyAssertions() ->tryToProve(Load::everythingIn(__DIR__.'/proofs/')) ->exit();