faisalmirza/diff-coverage

Calculate code coverage for git diff (changed lines only)

Installs: 56

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

Language:Shell

pkg:composer/faisalmirza/diff-coverage

v1.0.2 2026-01-25 17:12 UTC

This package is auto-updated.

Last update: 2026-01-25 17:16:09 UTC


README

diff-coverage

Latest Version PHP Version License

Ensure new code is tested. Check coverage for only the lines you changed.

diff-coverage runs your tests with coverage and validates that changed lines meet your coverage threshold. Stop debating overall coverage percentages—focus on what matters: new code should be tested.

./vendor/bin/diff-coverage
============================================
Diff Coverage Check
============================================
Branch:        origin/main
Threshold:     100%
Coverage file: tests/coverage/clover.xml
============================================

Running tests with coverage...
✓ 142 tests passed

Checking diff coverage...
============================================
Coverage: 94.2% (threshold: 100%)

Uncovered lines:
  src/Services/PaymentService.php:45
  src/Services/PaymentService.php:46

Why diff-coverage?

Traditional Coverage Diff Coverage
"We need 80% overall coverage" "New code must be tested"
Legacy code drags down metrics Only measures what you changed
Hard to improve incrementally Every PR can pass
Debates about thresholds Clear, actionable feedback

Requirements

  • PHP 8.1+
  • Git
  • Xdebug or PCOV (for coverage)

Installation

composer require --dev faisalmirza/diff-coverage

Note: If not yet on Packagist, install from GitHub:

{
    "repositories": [
        {"type": "vcs", "url": "https://github.com/faisalmirza/diff-coverage"}
    ],
    "require-dev": {
        "faisalmirza/diff-coverage": "dev-master"
    }
}

Quick Start

For Laravel, Pest, or PHPUnit projects, just run:

./vendor/bin/diff-coverage

The tool auto-detects your test framework and runs tests with coverage automatically.

Configuration

Configuration is loaded in layers (each layer overrides the previous):

Defaults → .diff-coverage.json → CLI arguments

Option 1: Zero Configuration (Recommended)

diff-coverage auto-detects your project type and enables parallel testing if paratest is installed:

Framework Detection Source Path Coverage Path
Laravel artisan file app, src tests/coverage/clover.xml
Symfony bin/console + composer.json src var/coverage/clover.xml
CakePHP bin/cake file src tmp/coverage/clover.xml
CodeIgniter spark file app build/coverage/clover.xml
Yii yii file src tests/coverage/clover.xml
Laminas config/application.config.php module, src data/coverage/clover.xml
Pest vendor/bin/pest src, app coverage/clover.xml
PHPUnit phpunit.xml or vendor/bin/phpunit src, app coverage/clover.xml

Parallel Testing: Automatically enabled when brianium/paratest is installed.

Option 2: Configuration File

Create .diff-coverage.json in your project root:

{
    "branch": "origin/main",
    "threshold": 80,
    "coverage_file": "tests/coverage/clover.xml",
    "test_cmd": "php artisan test --parallel --coverage-clover=tests/coverage/clover.xml",
    "source_paths": ["app", "src"]
}

All fields are optional. Only specify what you want to override.

Field Type Default Description
branch string origin/main Branch to compare against
threshold integer 100 Minimum coverage percentage for changed lines
coverage_file string (auto-detected) Path to Clover XML coverage file
test_cmd string (auto-detected) Command to run tests with coverage
source_paths array ["app", "src"] Directories to check for freshness

Option 3: CLI Arguments

Override any setting via command line:

./vendor/bin/diff-coverage [OPTIONS]
Option Description Example
-b, --branch Branch to compare -b origin/develop
-t, --threshold Coverage threshold -t 80
-c, --coverage Coverage file path -c build/coverage.xml
-T, --test-cmd Test command -T "phpunit --coverage-clover=cov.xml"
-f, --force Force re-run tests -f
-s, --skip-tests Use existing coverage -s
-h, --help Show help -h

Usage Examples

# Use auto-detected defaults
./vendor/bin/diff-coverage

# Compare against develop branch with 80% threshold
./vendor/bin/diff-coverage -b origin/develop -t 80

# Skip running tests (use existing coverage file)
./vendor/bin/diff-coverage -s

# Force re-run tests even if coverage file is fresh
./vendor/bin/diff-coverage -f

# Custom test command
./vendor/bin/diff-coverage -T "vendor/bin/phpunit --testsuite=unit --coverage-clover=coverage.xml"

Performance

Freshness Detection

diff-coverage automatically skips running tests if your coverage file is newer than your source files. This speeds up repeated runs during development.

$ ./vendor/bin/diff-coverage
Coverage file is fresh, skipping tests
(use --force to re-run tests)

To always run tests: ./vendor/bin/diff-coverage -f

To never run tests: ./vendor/bin/diff-coverage -s

CI Integration

GitHub Actions

name: Tests

on:
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # Required for git diff

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.3'
          coverage: xdebug

      - name: Install dependencies
        run: composer install

      - name: Check diff coverage
        run: ./vendor/bin/diff-coverage -t 80

GitLab CI

diff-coverage:
  stage: test
  script:
    - composer install
    - ./vendor/bin/diff-coverage -b origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME -t 80
  only:
    - merge_requests

Bitbucket Pipelines

pipelines:
  pull-requests:
    '**':
      - step:
          name: Diff Coverage
          script:
            - composer install
            - ./vendor/bin/diff-coverage -b origin/main -t 80

Exit Codes

Code Meaning
0 Coverage meets threshold (or no changes detected)
1 Coverage below threshold, tests failed, or configuration error

How It Works

  1. Detect your test framework (Laravel/Pest/PHPUnit)
  2. Run tests with coverage (unless skipped or fresh)
  3. Generate git diff against the target branch
  4. Filter coverage report to only changed lines
  5. Calculate coverage percentage for those lines
  6. Exit with success/failure based on threshold

Under the hood, diff-coverage uses exussum12/coverage-checker for the diff filtering logic.

Troubleshooting

"No changes detected"

Your branch has no diff against the target branch. This is normal for the main branch.

"Coverage file not found"

Tests may have failed, or the coverage file path is incorrect. Check:

  • Tests pass when run directly
  • The coverage file path in your config matches where your tests write it

Coverage seems wrong

Ensure you're comparing against the correct branch:

./vendor/bin/diff-coverage -b origin/main  # not just 'main'

Tests run every time

The freshness check compares timestamps. If your CI always clones fresh, tests will always run. This is expected behavior in CI.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT License. See LICENSE for details.