Simplify and improve how code coverage is generated in API and CLI tests

This commit is contained in:
Alejandro Celaya 2024-02-20 18:23:37 +01:00
parent a463e6f9d7
commit 8f6fc97fc8
6 changed files with 22 additions and 55 deletions

View File

@ -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

View File

@ -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*

View File

@ -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

View File

@ -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",

View File

@ -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'],

View File

@ -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),
],
] : [],
],