From 8f6fc97fc8797a885621ba581acf47ffcfbf2d07 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Tue, 20 Feb 2024 18:23:37 +0100 Subject: [PATCH] Simplify and improve how code coverage is generated in API and CLI tests --- .github/workflows/ci.yml | 5 ++-- CHANGELOG.md | 1 + bin/test/run-api-tests.sh | 2 +- composer.json | 11 ++++---- config/test/bootstrap_api_tests.php | 14 --------- config/test/test_config.global.php | 44 ++++++++--------------------- 6 files changed, 22 insertions(+), 55 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1777d139..3f33b0d1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -133,10 +133,9 @@ jobs: - run: mv build/coverage-db/coverage-db.cov build/coverage-db.cov - run: mv build/coverage-api/coverage-api.cov build/coverage-api.cov - run: mv build/coverage-cli/coverage-cli.cov build/coverage-cli.cov - - run: wget https://phar.phpunit.de/phpcov-10.0.0.phar - - run: php phpcov-10.0.0.phar merge build --clover build/clover.xml + - run: vendor/bin/phpcov merge build --clover build/clover.xml - name: Publish coverage - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v4 with: file: ./build/clover.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index cced1725..39797631 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this * [#1786](https://github.com/shlinkio/shlink/issues/1786) Run API tests with RoadRunner by default. * [#2008](https://github.com/shlinkio/shlink/issues/2008) Update to Doctrine ORM 3.0. * [#2010](https://github.com/shlinkio/shlink/issues/2010) Update to Symfony 7.0 components. +* [#2016](https://github.com/shlinkio/shlink/issues/2016) Simplify and improve how code coverage is generated in API and CLI tests". ### Deprecated * *Nothing* diff --git a/bin/test/run-api-tests.sh b/bin/test/run-api-tests.sh index 18ad2dd5..27fb1ae2 100755 --- a/bin/test/run-api-tests.sh +++ b/bin/test/run-api-tests.sh @@ -25,7 +25,7 @@ sleep 2 # Let's give the server a couple of seconds to start vendor/bin/phpunit --order-by=random -c phpunit-api.xml --testdox --colors=always --log-junit=build/coverage-api/junit.xml $* TESTS_EXIT_CODE=$? -[ "$TEST_RUNTIME" = 'rr' ] && bin/rr stop -c config/roadrunner/.rr.dev.yml -o=http.address=0.0.0.0:9999 +[ "$TEST_RUNTIME" = 'rr' ] && bin/rr stop -c=config/roadrunner/.rr.dev.yml -w . -o=http.address=0.0.0.0:9999 # Exit this script with the same code as the tests. If tests failed, this script has to fail exit $TESTS_EXIT_CODE diff --git a/composer.json b/composer.json index 8f9b86a6..d17c0631 100644 --- a/composer.json +++ b/composer.json @@ -68,10 +68,11 @@ "phpstan/phpstan-phpunit": "^1.3", "phpstan/phpstan-symfony": "^1.3", "phpunit/php-code-coverage": "^10.1", + "phpunit/phpcov": "^9.0", "phpunit/phpunit": "^10.4", "roave/security-advisories": "dev-master", "shlinkio/php-coding-standard": "~2.3.0", - "shlinkio/shlink-test-utils": "^3.11", + "shlinkio/shlink-test-utils": "^4.0", "symfony/var-dumper": "^7.0", "veewee/composer-run-parallel": "^1.3" }, @@ -128,11 +129,11 @@ "test:db:postgres": "DB_DRIVER=postgres composer test:db:sqlite", "test:db:ms": "DB_DRIVER=mssql composer test:db:sqlite", "test:api": "bin/test/run-api-tests.sh", - "test:api:ci": "GENERATE_COVERAGE=yes composer test:api", - "test:api:pretty": "GENERATE_COVERAGE=pretty composer test:api", + "test:api:ci": "GENERATE_COVERAGE=yes composer test:api && vendor/bin/phpcov merge build/coverage-api --xml build/coverage-api/coverage-xml --php build/coverage-api.cov && rm build/coverage-api/*.cov", + "test:api:pretty": "GENERATE_COVERAGE=yes composer test:api && vendor/bin/phpcov merge build/coverage-api --html build/coverage-api/coverage-html && rm build/coverage-api/*.cov", "test:cli": "APP_ENV=test DB_DRIVER=maria TEST_ENV=cli php vendor/bin/phpunit --order-by=random --colors=always --testdox -c phpunit-cli.xml --log-junit=build/coverage-cli/junit.xml", - "test:cli:ci": "GENERATE_COVERAGE=yes composer test:cli", - "test:cli:pretty": "GENERATE_COVERAGE=pretty composer test:cli", + "test:cli:ci": "GENERATE_COVERAGE=yes composer test:cli && vendor/bin/phpcov merge build/coverage-cli --xml build/coverage-cli/coverage-xml --php build/coverage-cli.cov && rm build/coverage-cli/*.cov", + "test:cli:pretty": "GENERATE_COVERAGE=yes composer test:cli && vendor/bin/phpcov merge build/coverage-cli --html build/coverage-cli/coverage-html && rm build/coverage-cli/*.cov", "infect:ci:base": "infection --threads=max --only-covered --skip-initial-tests", "infect:ci:unit": "@infect:ci:base --coverage=build/coverage-unit --min-msi=80", "infect:ci:db": "@infect:ci:base --coverage=build/coverage-db --min-msi=95 --configuration=infection-db.json5", diff --git a/config/test/bootstrap_api_tests.php b/config/test/bootstrap_api_tests.php index b82e5bc6..8f757c05 100644 --- a/config/test/bootstrap_api_tests.php +++ b/config/test/bootstrap_api_tests.php @@ -7,12 +7,6 @@ namespace Shlinkio\Shlink\TestUtils; use Doctrine\ORM\EntityManager; use Psr\Container\ContainerInterface; -use function register_shutdown_function; -use function sprintf; - -use const ShlinkioTest\Shlink\API_TESTS_HOST; -use const ShlinkioTest\Shlink\API_TESTS_PORT; - /** @var ContainerInterface $container */ $container = require __DIR__ . '/../container.php'; $testHelper = $container->get(Helper\TestHelper::class); @@ -20,14 +14,6 @@ $config = $container->get('config'); $em = $container->get(EntityManager::class); $httpClient = $container->get('shlink_test_api_client'); -// Dump code coverage when process shuts down -register_shutdown_function(function () use ($httpClient): void { - $httpClient->request( - 'GET', - sprintf('http://%s:%s/api-tests/stop-coverage', API_TESTS_HOST, API_TESTS_PORT), - ); -}); - $testHelper->createTestDb( createDbCommand: ['bin/cli', 'db:create'], migrateDbCommand: ['bin/cli', 'db:migrate'], diff --git a/config/test/test_config.global.php b/config/test/test_config.global.php index d86700b9..55f06bbf 100644 --- a/config/test/test_config.global.php +++ b/config/test/test_config.global.php @@ -6,7 +6,6 @@ namespace Shlinkio\Shlink; use GuzzleHttp\Client; use Laminas\ConfigAggregator\ConfigAggregator; -use Laminas\Diactoros\Response\EmptyResponse; use Laminas\Diactoros\Response\HtmlResponse; use Laminas\ServiceManager\Factory\InvokableFactory; use Mezzio\Router\FastRouteRouter; @@ -19,23 +18,26 @@ use Symfony\Component\Console\Application; use function Laminas\Stratigility\middleware; use function Shlinkio\Shlink\Config\env; -use function Shlinkio\Shlink\Core\ArrayUtils\contains; use function sleep; use function sprintf; use const ShlinkioTest\Shlink\API_TESTS_HOST; use const ShlinkioTest\Shlink\API_TESTS_PORT; -$isApiTest = env('TEST_ENV') === 'api'; -$isCliTest = env('TEST_ENV') === 'cli'; +$testEnv = env('TEST_ENV'); +$isApiTest = $testEnv === 'api'; +$isCliTest = $testEnv === 'cli'; $isE2eTest = $isApiTest || $isCliTest; $coverageType = env('GENERATE_COVERAGE'); -$generateCoverage = contains($coverageType, ['yes', 'pretty']); -$coverage = $isE2eTest && $generateCoverage ? CoverageHelper::createCoverageForDirectories([ - __DIR__ . '/../../module/Core/src', - __DIR__ . '/../../module/' . ($isApiTest ? 'Rest' : 'CLI') . '/src', -]) : null; +$generateCoverage = $coverageType === 'yes'; +$coverage = $isE2eTest && $generateCoverage ? CoverageHelper::createCoverageForDirectories( + [ + __DIR__ . '/../../module/Core/src', + __DIR__ . '/../../module/' . ($isApiTest ? 'Rest' : 'CLI') . '/src', + ], + __DIR__ . '/../../build/coverage-' . $testEnv, +) : null; $buildDbConnection = static function (): array { $driver = env('DB_DRIVER', 'sqlite'); @@ -99,23 +101,6 @@ return [ ], 'routes' => [ - // This route is invoked at the end of API tests, in order to dump coverage collected so far - [ - 'name' => 'dump_coverage', - 'path' => '/api-tests/stop-coverage', - 'allowed_methods' => ['GET'], - 'middleware' => middleware(static function () use ($coverage, $coverageType) { - // TODO I have tried moving this block to a register_shutdown_function here, which internally checks if - // RR_MODE === 'http', but this seems to be false in CI, causing the coverage to not be generated - CoverageHelper::exportCoverage( - $coverage, - __DIR__ . '/../../build/coverage-api', - pretty: $coverageType === 'pretty', - ); - return new EmptyResponse(); - }), - ], - // This route is used to test that title resolution is skipped if the long URL times out [ 'name' => 'long_url_with_timeout', @@ -154,12 +139,7 @@ return [ ], 'delegators' => $isCliTest ? [ Application::class => [ - new CliCoverageDelegator(fn () => CoverageHelper::exportCoverage( - $coverage, - __DIR__ . '/../../build/coverage-cli', - pretty: $coverageType === 'pretty', - mergeWithExisting: true, - ), $coverage), + new CliCoverageDelegator($coverage), ], ] : [], ],