diff --git a/.gitattributes b/.gitattributes index eaddab84c..3bf0f2918 100644 --- a/.gitattributes +++ b/.gitattributes @@ -10,9 +10,11 @@ /.gitignore export-ignore /CODE_OF_CONDUCT.md export-ignore /CONTRIBUTING.md export-ignore +/MAINTAINERS.md export-ignore /README.md export-ignore /UPGRADING.md export-ignore /phpcs.xml.dist export-ignore /phpstan.neon.dist export-ignore /phpunit.xml.dist export-ignore +/psalm.xml export-ignore /tests export-ignore diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 87dbf99fa..e7fd8a78e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,5 +1,8 @@ name: Tests +permissions: + contents: read + on: [push, pull_request] jobs: @@ -12,13 +15,17 @@ jobs: matrix: php: [7.4, 8.0, 8.1, 8.2, 8.3, 8.4] experimental: [false] + composer-options: [''] include: - php: 8.2 analysis: true + - php: nightly + experimental: true + composer-options: '--ignore-platform-req=php+' steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up PHP ${{ matrix.php }} uses: shivammathur/setup-php@v2 @@ -27,12 +34,7 @@ jobs: coverage: xdebug - name: Install dependencies with Composer - if: matrix.php < '8.4' - run: composer update --prefer-dist --no-progress --no-interaction --ansi - - - name: Install dependencies with Composer - if: matrix.php >= '8.4' - run: composer update --prefer-dist --no-progress --no-interaction --ansi --ignore-platform-reqs + run: composer update --prefer-dist --no-progress --no-interaction --ansi ${{ matrix.composer-options }} - name: Coding standards if: matrix.analysis @@ -43,12 +45,11 @@ jobs: run: vendor/bin/phpstan - name: Tests - run: vendor/bin/phpunit --coverage-clover clover.xml + run: vendor/bin/phpunit ${{ matrix.analysis && '--coverage-clover clover.xml' || '--no-coverage' }} - name: Upload coverage results to Coveralls if: matrix.analysis - env: - COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - composer require php-coveralls/php-coveralls -n -W - vendor/bin/php-coveralls --coverage_clover=clover.xml -v + uses: coverallsapp/github-action@v2 + with: + flag-name: php-${{ matrix.php }} + files: clover.xml diff --git a/composer.json b/composer.json index 6ff4d8b4d..aa966cab3 100644 --- a/composer.json +++ b/composer.json @@ -65,7 +65,7 @@ "phpspec/prophecy": "^1.19", "phpspec/prophecy-phpunit": "^2.1", "phpstan/phpstan": "^1 || ^2", - "phpunit/phpunit": "^9.6", + "phpunit/phpunit": "^9.6 || ^10 || ^11 || ^12", "slim/http": "^1.3", "slim/psr7": "^1.6", "squizlabs/php_codesniffer": "^3.10", diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 2bbfaf422..5c837c614 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,24 +1,27 @@ tests + tests/Mocks - - - - Slim - + + + + Slim + + diff --git a/tests/AppTest.php b/tests/AppTest.php index 80c6e9f66..1724695d6 100644 --- a/tests/AppTest.php +++ b/tests/AppTest.php @@ -155,7 +155,7 @@ public function testGetMiddlewareDispatcherGetsSeededAndReturnsInjectedInstance( $this->assertSame($middlewareDispatcherProphecy->reveal(), $app->getMiddlewareDispatcher()); } - public function lowerCaseRequestMethodsProvider(): array + public static function lowerCaseRequestMethodsProvider(): array { return [ ['get'], @@ -171,6 +171,7 @@ public function lowerCaseRequestMethodsProvider(): array * @param string $method * @dataProvider upperCaseRequestMethodsProvider() */ + #[\PHPUnit\Framework\Attributes\DataProvider('upperCaseRequestMethodsProvider')] public function testGetPostPutPatchDeleteOptionsMethods(string $method): void { $streamProphecy = $this->prophesize(StreamInterface::class); @@ -244,7 +245,7 @@ public function testAnyRoute(): void * Route collector proxy methods *******************************************************************************/ - public function upperCaseRequestMethodsProvider(): array + public static function upperCaseRequestMethodsProvider(): array { return [ ['GET'], @@ -261,6 +262,8 @@ public function upperCaseRequestMethodsProvider(): array * @dataProvider lowerCaseRequestMethodsProvider * @dataProvider upperCaseRequestMethodsProvider */ + #[\PHPUnit\Framework\Attributes\DataProvider('lowerCaseRequestMethodsProvider')] + #[\PHPUnit\Framework\Attributes\DataProvider('upperCaseRequestMethodsProvider')] public function testMapRoute(string $method): void { $streamProphecy = $this->prophesize(StreamInterface::class); @@ -376,7 +379,7 @@ public function testRouteWithInternationalCharacters(): void * Route Patterns *******************************************************************************/ - public function routePatternsProvider(): array + public static function routePatternsProvider(): array { return [ [''], // Empty Route @@ -391,6 +394,7 @@ public function routePatternsProvider(): array * @param string $pattern * @dataProvider routePatternsProvider */ + #[\PHPUnit\Framework\Attributes\DataProvider('routePatternsProvider')] public function testRoutePatterns(string $pattern): void { $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); @@ -409,7 +413,7 @@ public function testRoutePatterns(string $pattern): void * Route Groups *******************************************************************************/ - public function routeGroupsDataProvider(): array + public static function routeGroupsDataProvider(): array { return [ 'empty group with empty route' => [ @@ -539,6 +543,7 @@ public function testGroupClosureIsBoundToThisClass(): void * @param array $sequence * @param string $expectedPath */ + #[\PHPUnit\Framework\Attributes\DataProvider('routeGroupsDataProvider')] public function testRouteGroupCombinations(array $sequence, string $expectedPath): void { $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); diff --git a/tests/Factory/AppFactoryTest.php b/tests/Factory/AppFactoryTest.php index 25349ffc8..700c85ebf 100644 --- a/tests/Factory/AppFactoryTest.php +++ b/tests/Factory/AppFactoryTest.php @@ -50,7 +50,7 @@ protected function tearDown(): void $reflectionClass->setStaticPropertyValue('responseFactoryClass', DecoratedResponseFactory::class); } - public function provideImplementations() + public static function provideImplementations() { return [ [SlimPsr17Factory::class, SlimResponseFactory::class], @@ -66,6 +66,7 @@ public function provideImplementations() * @param string $psr17factory * @param string $expectedResponseFactoryClass */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideImplementations')] public function testCreateAppWithAllImplementations(string $psr17factory, string $expectedResponseFactoryClass) { Psr17FactoryProvider::setFactories([$psr17factory]); @@ -134,6 +135,7 @@ public function testSetPsr17FactoryProvider() /** * @runInSeparateProcess - Psr17FactoryProvider::setFactories breaks other tests */ + #[\PHPUnit\Framework\Attributes\RunInSeparateProcess] public function testResponseFactoryIsStillReturnedIfStreamFactoryIsNotAvailable() { Psr17FactoryProvider::setFactories([MockPsr17FactoryWithoutStreamFactory::class]); @@ -147,6 +149,7 @@ public function testResponseFactoryIsStillReturnedIfStreamFactoryIsNotAvailable( /** * @runInSeparateProcess - AppFactory::setResponseFactory breaks other tests */ + #[\PHPUnit\Framework\Attributes\RunInSeparateProcess] public function testAppIsCreatedWithInstancesFromSetters() { $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); @@ -204,6 +207,7 @@ public function testAppIsCreatedWithInstancesFromSetters() * @runInSeparateProcess - AppFactory::create saves $responseFactory into static::$responseFactory, * this breaks other tests */ + #[\PHPUnit\Framework\Attributes\RunInSeparateProcess] public function testAppIsCreatedWithInjectedInstancesFromFunctionArguments() { $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); @@ -254,6 +258,7 @@ public function testAppIsCreatedWithInjectedInstancesFromFunctionArguments() /** * @runInSeparateProcess - AppFactory::setResponseFactory breaks other tests */ + #[\PHPUnit\Framework\Attributes\RunInSeparateProcess] public function testResponseAndStreamFactoryIsBeingInjectedInDecoratedResponseFactory() { $responseProphecy = $this->prophesize(ResponseInterface::class); diff --git a/tests/Factory/Psr17/Psr17FactoryProviderTest.php b/tests/Factory/Psr17/Psr17FactoryProviderTest.php index 291000ddb..144a13376 100644 --- a/tests/Factory/Psr17/Psr17FactoryProviderTest.php +++ b/tests/Factory/Psr17/Psr17FactoryProviderTest.php @@ -18,6 +18,7 @@ class Psr17FactoryProviderTest extends TestCase /** * @runInSeparateProcess - Psr17FactoryProvider::setFactories breaks other tests */ + #[\PHPUnit\Framework\Attributes\RunInSeparateProcess] public function testGetSetFactories() { Psr17FactoryProvider::setFactories([]); @@ -29,6 +30,7 @@ public function testGetSetFactories() /** * @runInSeparateProcess - Psr17FactoryProvider::setFactories breaks other tests */ + #[\PHPUnit\Framework\Attributes\RunInSeparateProcess] public function testAddFactory() { Psr17FactoryProvider::setFactories(['Factory 1']); diff --git a/tests/Factory/ServerRequestCreatorFactoryTest.php b/tests/Factory/ServerRequestCreatorFactoryTest.php index e68a08382..8c25d126c 100644 --- a/tests/Factory/ServerRequestCreatorFactoryTest.php +++ b/tests/Factory/ServerRequestCreatorFactoryTest.php @@ -31,7 +31,7 @@ class ServerRequestCreatorFactoryTest extends TestCase { - public function provideImplementations() + public static function provideImplementations() { return [ [SlimPsr17Factory::class, SlimServerRequest::class], @@ -47,6 +47,7 @@ public function provideImplementations() * @param string $psr17factory * @param string $expectedServerRequestClass */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideImplementations')] public function testCreateAppWithAllImplementations(string $psr17factory, string $expectedServerRequestClass) { Psr17FactoryProvider::setFactories([$psr17factory]); @@ -72,6 +73,7 @@ public function testDetermineServerRequestCreatorReturnsDecoratedServerRequestCr /** * @runInSeparateProcess - Psr17FactoryProvider::setFactories breaks other tests */ + #[\PHPUnit\Framework\Attributes\RunInSeparateProcess] public function testDetermineServerRequestCreatorThrowsRuntimeException() { $this->expectException(RuntimeException::class); @@ -96,6 +98,7 @@ public function testSetPsr17FactoryProvider() /** * @runInSeparateProcess - ServerRequestCreatorFactory::setServerRequestCreator breaks other tests */ + #[\PHPUnit\Framework\Attributes\RunInSeparateProcess] public function testSetServerRequestCreatorWithoutDecorators() { ServerRequestCreatorFactory::setSlimHttpDecoratorsAutomaticDetection(false); @@ -117,6 +120,7 @@ public function testSetServerRequestCreatorWithoutDecorators() /** * @runInSeparateProcess - ServerRequestCreatorFactory::setServerRequestCreator breaks other tests */ + #[\PHPUnit\Framework\Attributes\RunInSeparateProcess] public function testSetServerRequestCreatorWithDecorators() { ServerRequestCreatorFactory::setSlimHttpDecoratorsAutomaticDetection(true); diff --git a/tests/Handlers/ErrorHandlerTest.php b/tests/Handlers/ErrorHandlerTest.php index b50dff4b3..296e38b8a 100644 --- a/tests/Handlers/ErrorHandlerTest.php +++ b/tests/Handlers/ErrorHandlerTest.php @@ -36,10 +36,7 @@ private function getMockLogger(): LoggerInterface public function testDetermineRenderer() { - $handler = $this - ->getMockBuilder(ErrorHandler::class) - ->disableOriginalConstructor() - ->getMock(); + $handler = $this->createMock(ErrorHandler::class); $class = new ReflectionClass(ErrorHandler::class); $callableResolverProperty = $class->getProperty('callableResolver'); @@ -77,10 +74,7 @@ public function testDetermineRenderer() public function testDetermineStatusCode() { $request = $this->createServerRequest('/'); - $handler = $this - ->getMockBuilder(ErrorHandler::class) - ->disableOriginalConstructor() - ->getMock(); + $handler = $this->createMock(ErrorHandler::class); $class = new ReflectionClass(ErrorHandler::class); $reflectionProperty = $class->getProperty('responseFactory'); @@ -129,10 +123,7 @@ public function testHalfValidContentType() ->createServerRequest('/', 'GET') ->withHeader('Content-Type', 'unknown/json+'); - $handler = $this - ->getMockBuilder(ErrorHandler::class) - ->disableOriginalConstructor() - ->getMock(); + $handler = $this->createMock(ErrorHandler::class); $newErrorRenderers = [ 'application/xml' => XmlErrorRenderer::class, 'text/xml' => XmlErrorRenderer::class, @@ -164,10 +155,7 @@ public function testDetermineContentTypeTextPlainMultiAcceptHeader() ->withHeader('Content-Type', 'text/plain') ->withHeader('Accept', 'text/plain,text/xml'); - $handler = $this - ->getMockBuilder(ErrorHandler::class) - ->disableOriginalConstructor() - ->getMock(); + $handler = $this->createMock(ErrorHandler::class); $errorRenderers = [ 'text/plain' => PlainTextErrorRenderer::class, @@ -199,10 +187,7 @@ public function testDetermineContentTypeApplicationJsonOrXml() ->withHeader('Content-Type', 'text/json') ->withHeader('Accept', 'application/xhtml+xml'); - $handler = $this - ->getMockBuilder(ErrorHandler::class) - ->disableOriginalConstructor() - ->getMock(); + $handler = $this->createMock(ErrorHandler::class); $errorRenderers = [ 'application/xml' => XmlErrorRenderer::class @@ -242,10 +227,7 @@ public function testAcceptableMediaTypeIsNotFirstInList() $method->setAccessible(true); // use a mock object here as ErrorHandler cannot be directly instantiated - $handler = $this - ->getMockBuilder(ErrorHandler::class) - ->disableOriginalConstructor() - ->getMock(); + $handler = $this->createMock(ErrorHandler::class); // call determineContentType() $return = $method->invoke($handler, $request); diff --git a/tests/Middleware/BodyParsingMiddlewareTest.php b/tests/Middleware/BodyParsingMiddlewareTest.php index 30cba022c..c6f11512d 100644 --- a/tests/Middleware/BodyParsingMiddlewareTest.php +++ b/tests/Middleware/BodyParsingMiddlewareTest.php @@ -64,7 +64,7 @@ protected function createRequestWithBody($contentType, $body) } - public function parsingProvider() + public static function parsingProvider() { return [ 'form' => [ @@ -148,6 +148,7 @@ public function parsingProvider() /** * @dataProvider parsingProvider */ + #[\PHPUnit\Framework\Attributes\DataProvider('parsingProvider')] public function testParsing($contentType, $body, $expected) { $request = $this->createRequestWithBody($contentType, $body); diff --git a/tests/MiddlewareDispatcherTest.php b/tests/MiddlewareDispatcherTest.php index ad87fedfc..be1cc12f3 100644 --- a/tests/MiddlewareDispatcherTest.php +++ b/tests/MiddlewareDispatcherTest.php @@ -138,7 +138,7 @@ public function testDeferredResolvedCallableWithDirectConstructorCall(): void $this->assertSame(1, $handler->getCalledCount()); } - public function deferredCallableProvider(): array + public static function deferredCallableProvider(): array { return [ [MockMiddlewareSlimCallable::class . ':custom', new MockMiddlewareSlimCallable()], @@ -157,6 +157,7 @@ public function deferredCallableProvider(): array * @param string $callable * @param callable|MiddlewareInterface */ + #[\PHPUnit\Framework\Attributes\DataProvider('deferredCallableProvider')] public function testDeferredResolvedCallableWithContainerAndNonAdvancedCallableResolverUnableToResolveCallable( $callable, $result diff --git a/tests/Routing/FastRouteDispatcherTest.php b/tests/Routing/FastRouteDispatcherTest.php index 0f49c42d9..48e575ac4 100644 --- a/tests/Routing/FastRouteDispatcherTest.php +++ b/tests/Routing/FastRouteDispatcherTest.php @@ -23,6 +23,7 @@ class FastRouteDispatcherTest extends TestCase /** * @dataProvider provideFoundDispatchCases */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideFoundDispatchCases')] public function testFoundDispatches($method, $uri, $callback, $handler, $argDict) { /** @var FastRouteDispatcher $dispatcher */ @@ -59,6 +60,7 @@ protected function getDispatcherClass() /** * @dataProvider provideNotFoundDispatchCases */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideNotFoundDispatchCases')] public function testNotFoundDispatches($method, $uri, $callback) { /** @var FastRouteDispatcher $dispatcher */ @@ -74,8 +76,10 @@ public function testNotFoundDispatches($method, $uri, $callback) * @param $method * @param $uri * @param $callback + * @param $allowedMethods */ - public function testMethodNotAllowedDispatches($method, $uri, $callback) + #[\PHPUnit\Framework\Attributes\DataProvider('provideMethodNotAllowedDispatchCases')] + public function testMethodNotAllowedDispatches($method, $uri, $callback, $allowedMethods) { /** @var FastRouteDispatcher $dispatcher */ $dispatcher = simpleDispatcher($callback, $this->generateDispatcherOptions()); @@ -92,6 +96,7 @@ public function testMethodNotAllowedDispatches($method, $uri, $callback) * @param $callback * @param $allowedMethods */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideMethodNotAllowedDispatchCases')] public function testGetAllowedMethods($method, $uri, $callback, $allowedMethods) { /** @var FastRouteDispatcher $dispatcher */ @@ -160,7 +165,7 @@ public function testCapturing() }, $this->generateDispatcherOptions()); } - public function provideFoundDispatchCases() + public static function provideFoundDispatchCases() { $cases = []; @@ -459,7 +464,7 @@ public function provideFoundDispatchCases() return $cases; } - public function provideNotFoundDispatchCases() + public static function provideNotFoundDispatchCases() { $cases = []; @@ -536,7 +541,7 @@ public function provideNotFoundDispatchCases() return $cases; } - public function provideMethodNotAllowedDispatchCases() + public static function provideMethodNotAllowedDispatchCases() { $cases = []; diff --git a/tests/Routing/RouteContextTest.php b/tests/Routing/RouteContextTest.php index 871843792..7dfec8ecf 100644 --- a/tests/Routing/RouteContextTest.php +++ b/tests/Routing/RouteContextTest.php @@ -70,7 +70,7 @@ public function testCanCreateInstanceWithoutBasePathAndThrowExceptionIfGetBasePa $routeContext->getBasePath(); } - public function requiredRouteContextRequestAttributes(): array + public static function requiredRouteContextRequestAttributes(): array { return [ [RouteContext::ROUTE_PARSER], @@ -82,6 +82,7 @@ public function requiredRouteContextRequestAttributes(): array * @dataProvider requiredRouteContextRequestAttributes * @param string $attribute */ + #[\PHPUnit\Framework\Attributes\DataProvider('requiredRouteContextRequestAttributes')] public function testCannotCreateInstanceIfRequestIsMissingAttributes(string $attribute): void { $this->expectException(RuntimeException::class); diff --git a/tests/Routing/RouteParserTest.php b/tests/Routing/RouteParserTest.php index 839575f6a..e16036620 100644 --- a/tests/Routing/RouteParserTest.php +++ b/tests/Routing/RouteParserTest.php @@ -17,7 +17,7 @@ class RouteParserTest extends TestCase { - public function urlForCases() + public static function urlForCases() { return [ 'with base path' => [ @@ -122,6 +122,7 @@ public function testBasePathIsIgnoreInRelativePathFor() * @param $queryParams * @param $expectedResult */ + #[\PHPUnit\Framework\Attributes\DataProvider('urlForCases')] public function testUrlForWithBasePath($withBasePath, $pattern, $arguments, $queryParams, $expectedResult) { $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); diff --git a/tests/Routing/RouteResolverTest.php b/tests/Routing/RouteResolverTest.php index 2a3a7e288..efb3df06a 100644 --- a/tests/Routing/RouteResolverTest.php +++ b/tests/Routing/RouteResolverTest.php @@ -23,7 +23,7 @@ class RouteResolverTest extends TestCase { - public function computeRoutingResultsDataProvider(): array + public static function computeRoutingResultsDataProvider(): array { return [ ['GET', '', '/'], @@ -40,6 +40,7 @@ public function computeRoutingResultsDataProvider(): array * @param string $uri The request uri * @param string $expectedUri The expected uri after transformation in the computeRoutingResults() */ + #[\PHPUnit\Framework\Attributes\DataProvider('computeRoutingResultsDataProvider')] public function testComputeRoutingResults(string $method, string $uri, string $expectedUri) { $routeCollectorProphecy = $this->prophesize(RouteCollectorInterface::class);