├── .github └── workflows │ └── actions.yml ├── .gitignore ├── LICENSE ├── README.md ├── bin └── coverage-check ├── composer.json ├── composer.lock ├── coverage-check.php └── test ├── clover.xml ├── empty.xml └── run /.github/workflows/actions.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | build: 7 | name: Tests PHP ${{ matrix.php-version }} 8 | runs-on: ubuntu-20.04 9 | strategy: 10 | matrix: 11 | php-version: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0'] 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Setup PHP ${{ matrix.php-version }} 17 | uses: shivammathur/setup-php@v2 18 | with: 19 | php-version: ${{ matrix.php-version }} 20 | 21 | - run: composer install --prefer-dist --no-progress --no-suggest 22 | - run: ./test/run -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Richard Regeer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/richardregeer/phpunit-coverage-check.svg?branch=master)](https://travis-ci.org/richardregeer/phpunit-coverage-check) 2 | 3 | # phpunit-coverage-check 4 | This php script will read the clover xml report from phpunit and calculates the coverage score. Based on the given threshold the script will exit ok of the coverage is higher then the threshold or exit with code 1 if the coverage is lower then the threshold. 5 | This script can be used in your continuous deployment environment or for example added to a pre-commit hook. 6 | 7 | # Installation 8 | The script can be installed using composer. Add this repository as a dependency to the composer.json file. 9 | 10 | ```bash 11 | composer require --dev rregeer/phpunit-coverage-check 12 | ``` 13 | 14 | # Usage 15 | The script has requires 2 parameters that are mandatory to return the code coverage. 16 | 17 | 1. The location of the clover xml file, that's generated by phpunit. 18 | 2. The coverage threshold that is acceptable. Min = 1, Max = 100 19 | 20 | Generate the `clover.xml` file by using phpunit and run the coverage check script: 21 | Run the script: 22 | 23 | ```bash 24 | vendor/bin/phpunit --coverage-clover clover.xml 25 | vendor/bin/coverage-check clover.xml 80 26 | vendor/bin/coverage-check clover.xml 80 --only-percentage 27 | ``` 28 | 29 | With the `--only-percentage` enabled, the CLI command will only return the resulting coverage percentage. 30 | 31 | It's also possible to add the coverage report generation to the `phpunit.xml.dist` add to following lines to the xml file inside the `` tag: 32 | 33 | ```xml 34 | 35 | 36 | 37 | ``` 38 | 39 | For more information see the [phpunit documentation](https://phpunit.readthedocs.io/en/9.5/). 40 | Information about the [configuration file](https://phpunit.readthedocs.io/en/9.5/configuration.html) and [commandline options](https://phpunit.readthedocs.io/en/9.5/textui.html#command-line-options). 41 | -------------------------------------------------------------------------------- /bin/coverage-check: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | =7.0.0" 21 | }, 22 | "bin": ["bin/coverage-check"] 23 | } 24 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "a9497a69ebd8915d88ea719f570f9441", 8 | "packages": [], 9 | "packages-dev": [], 10 | "aliases": [], 11 | "minimum-stability": "stable", 12 | "stability-flags": [], 13 | "prefer-stable": false, 14 | "prefer-lowest": false, 15 | "platform": { 16 | "php": ">=7.0.0" 17 | }, 18 | "platform-dev": [] 19 | } 20 | -------------------------------------------------------------------------------- /coverage-check.php: -------------------------------------------------------------------------------- 1 | xpath(XPATH_METRICS); 21 | } 22 | 23 | function printStatus(string $msg, int $exitCode = STATUS_OK) 24 | { 25 | echo $msg.PHP_EOL; 26 | exit($exitCode); 27 | } 28 | 29 | if (! isset($argv[1]) || ! file_exists($argv[1])) { 30 | printStatus("Invalid input file {$argv[1]} provided.", STATUS_ERROR); 31 | } 32 | 33 | if (! isset($argv[2])) { 34 | printStatus( 35 | 'An integer checked percentage must be given as second parameter.', 36 | STATUS_ERROR 37 | ); 38 | } 39 | 40 | $onlyEchoPercentage = isset($argv[3]) && $argv[3] === '--only-percentage'; 41 | 42 | $inputFile = $argv[1]; 43 | $percentage = min(100, max(0, (float) $argv[2])); 44 | 45 | $elements = 0; 46 | $coveredElements = 0; 47 | $statements = 0; 48 | $coveredstatements = 0; 49 | $methods = 0; 50 | $coveredmethods = 0; 51 | 52 | foreach (loadMetrics($inputFile) as $metric) { 53 | $elements += (int) $metric['elements']; 54 | $coveredElements += (int) $metric['coveredelements']; 55 | $statements += (int) $metric['statements']; 56 | $coveredstatements += (int) $metric['coveredstatements']; 57 | $methods += (int) $metric['methods']; 58 | $coveredmethods += (int) $metric['coveredmethods']; 59 | } 60 | 61 | // See calculation: https://confluence.atlassian.com/pages/viewpage.action?pageId=79986990 62 | $coveredMetrics = $coveredstatements + $coveredmethods + $coveredElements; 63 | $totalMetrics = $statements + $methods + $elements; 64 | 65 | if ($totalMetrics === 0) { 66 | printStatus('Insufficient data for calculation. Please add more code.', STATUS_ERROR); 67 | } 68 | 69 | $totalPercentageCoverage = $coveredMetrics / $totalMetrics * 100; 70 | 71 | if ($totalPercentageCoverage < $percentage && ! $onlyEchoPercentage) { 72 | printStatus( 73 | 'Total code coverage is '.formatCoverage($totalPercentageCoverage).' which is below the accepted '.$percentage.'%', 74 | STATUS_ERROR 75 | ); 76 | } 77 | 78 | if ($totalPercentageCoverage < $percentage && $onlyEchoPercentage) { 79 | printStatus(formatCoverage($totalPercentageCoverage), STATUS_ERROR); 80 | } 81 | 82 | if ($onlyEchoPercentage) { 83 | printStatus(formatCoverage($totalPercentageCoverage)); 84 | } 85 | 86 | printStatus('Total code coverage is '.formatCoverage($totalPercentageCoverage).' – OK!'); 87 | -------------------------------------------------------------------------------- /test/clover.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /test/empty.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /test/run: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | # Consistent CWD, regardless of which directory the script is run from. 4 | cd $(dirname $0)/.. 5 | 6 | # Expect pass 7 | bin/coverage-check test/clover.xml 0 8 | bin/coverage-check test/clover.xml 90 9 | 10 | # Expect fail 11 | ! bin/coverage-check test/empty.xml 10 12 | ! bin/coverage-check test/clover.xml 95 13 | ! bin/coverage-check test/clover.xml 100 14 | 15 | # Only percentage 16 | expected="90.32 %" 17 | actual=$(bin/coverage-check test/clover.xml 90 --only-percentage) 18 | 19 | [[ "$expected" == "$actual" ]] || (echo "ERROR: Expected coverage $expected, got $actual" && exit 1) 20 | --------------------------------------------------------------------------------