diff --git a/.github/workflows/compute.yml b/.github/workflows/compute.yml new file mode 100644 index 0000000..c6d1c99 --- /dev/null +++ b/.github/workflows/compute.yml @@ -0,0 +1,54 @@ +name: ✏️ matrix + +on: + workflow_call: + outputs: + os: + value: ${{ jobs.compute.outputs.os }} + coverage: + value: ${{ jobs.compute.outputs.coverage }} + major: + value: ${{ jobs.compute.outputs.major }} + php: + value: ${{ jobs.compute.outputs.php }} + exclude: + value: ${{ jobs.compute.outputs.exclude }} + +env: + OS: '[ "ubuntu-latest" ]' + COVERAGE: '[ "~11.5.27", "~12.2.7" ]' + PHP: '[ "8.3", "8.4" ]' + EXCLUDE: '[ { "coverage": "~11.5.27", "php": "8.3" }, { "coverage": "~11.5.27", "php": "8.4" }, { "coverage": "~12.2.7", "php": "8.3" }, { "coverage": "~12.2.7", "php": "8.4" } ]' + +jobs: + compute: + name: Compute outputs + + runs-on: ubuntu-latest + + outputs: + os: ${{ env.OS }} + coverage: ${{ env.COVERAGE }} + major: ${{ steps.major-version.outputs.major }} + php: ${{ env.PHP }} + exclude: ${{ env.EXCLUDE }} + + steps: + - name: Compute major versions + id: major-version + run: | + echo -e "COVERAGE\n" + echo $COVERAGE + echo -e "\n\nSplit by comma\n" + echo $COVERAGE | tr "," "\n" + echo -e "\n\nParse numbers\n" + echo $COVERAGE | tr "," "\n" | tr -cd "\n0-9" + echo -e "\n\nCut last 2 characters\n" + echo $COVERAGE | tr "," "\n" | tr -cd "\n0-9" | sed "s/.\{2\}$//" + echo -e "\n\nSort by version\n" + echo $COVERAGE | tr "," "\n" | tr -cd "\n0-9" | sed "s/.\{2\}$//" | sort -V + echo -e "\n\nUnique values only\n" + echo $COVERAGE | tr "," "\n" | tr -cd "\n0-9" | sed "s/.\{2\}$//" | sort -V | uniq + echo -e "\n\nCovert to JSON\n" + echo $COVERAGE | tr "," "\n" | tr -cd "\n0-9" | sed "s/.\{2\}$//" | sort -V | uniq | jq --compact-output --raw-input --slurp 'split("\n") | map(select(. != ""))' + echo "major=$(echo $COVERAGE | tr "," "\n" | tr -cd "\n0-9" | sed "s/.\{2\}$//" | sort -V | uniq | jq --compact-output --raw-input --slurp 'split("\n") | map(select(. != ""))')" >> $GITHUB_OUTPUT diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..caf2671 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,163 @@ +name: 🏃 tests + +on: [ push, pull_request, workflow_call ] + +jobs: + compute: + uses: ./.github/workflows/compute.yml + + build: + name: 'Build COVERAGE: ${{ matrix.coverage }} - PHP: ${{ matrix.php }}' + + needs: [ compute ] + + strategy: + fail-fast: false + matrix: + os: ${{ fromJson(needs.compute.outputs.os) }} + coverage: ${{ fromJson(needs.compute.outputs.coverage) }} + php: ${{ fromJson(needs.compute.outputs.php) }} + exclude: ${{ fromJson(needs.compute.outputs.exclude) }} + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Store Composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Store PHP code coverage version + id: version-cache + env: + COVERAGE: ${{ matrix.coverage }} + run: | + echo "version=$(echo $COVERAGE | tr -d -c 0-9)" >> $GITHUB_OUTPUT + echo "major=$(echo $COVERAGE | tr -d -c 0-9 | sed 's/.\{2\}$//')" >> $GITHUB_OUTPUT + + - uses: actions/cache/restore@v4 + id: restore-composer-cache + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-${{ matrix.php }}-${{ steps.version-cache.outputs.major }}-${{ matrix.coverage }} + restore-keys: | + ${{ runner.os }}-${{ matrix.php }}-${{ steps.version-cache.outputs.major }}- + ${{ runner.os }}-${{ matrix.php }}- + ${{ runner.os }}- + + - name: Set up PHP Version ${{ matrix.php }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: xdebug + tools: composer:v2 + + - name: Environment Check + run: | + php --version + composer --version + mkdir -p .Log/coverage/ .Log/log/ + + - name: Validate composer.json + run: composer validate + + - name: Composer install + run: composer update --with "phpunit/php-code-coverage:${{ matrix.coverage }}" --no-interaction + + - name: Save composer cache + uses: actions/cache/save@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ steps.restore-composer-cache.outputs.cache-primary-key }} + + - name: Lint PHP + run: php .Build/bin/parallel-lint --exclude .Build . + + - name: Run PHPUnit + if: ${{ success() || failure() }} + run: find 'tests' -wholename '*Test.php' | parallel --gnu 'echo -e "\n\nRunning test {}"; HASH=${{ steps.version-cache.outputs.version }}_$( echo {} | md5sum | cut -d " " -f 1); .Build/bin/phpunit --log-junit .Log/log/junit_$HASH.xml --coverage-php .Log/coverage/coverage_$HASH.cov --coverage-filter src/ {}' + + - name: Archive PHPUnit logs + uses: actions/upload-artifact@v4 + with: + name: phpunit-logs-${{ runner.os }}-${{ matrix.php }}-${{ steps.version-cache.outputs.major }}-${{ matrix.coverage }} + path: .Log/* + retention-days: 1 + + merge: + name: 'Merge COVERAGE: ${{ matrix.coverage }} - PHP: ${{ matrix.php }}' + + needs: [ compute, build ] + + strategy: + fail-fast: false + matrix: + os: ${{ fromJson(needs.compute.outputs.os) }} + coverage: ${{ fromJson(needs.compute.outputs.major) }} + php: ${{ fromJson(needs.compute.outputs.php) }} + exclude: ${{ fromJson(needs.compute.outputs.exclude) }} + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Download PHPUnit logs + uses: actions/download-artifact@v4 + with: + path: .Log + pattern: phpunit-logs-${{ runner.os }}-${{ matrix.php }}-${{ matrix.coverage }}-* + merge-multiple: true + + - name: Store Composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - uses: actions/cache/restore@v4 + id: restore-composer-cache + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.coverage }} + restore-keys: | + ${{ runner.os }}-${{ matrix.php }}- + ${{ runner.os }}- + + - name: Set up PHP Version ${{ matrix.php }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: xdebug + tools: composer:v2 + + - name: Environment Check + run: | + php --version + composer --version + + - name: Validate composer.json + run: composer validate + + - name: Composer install + run: composer update --with "phpunit/php-code-coverage:^${{ matrix.coverage }}.0" --no-interaction + + - name: Save composer cache + uses: actions/cache/save@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ steps.restore-composer-cache.outputs.cache-primary-key }} + + - name: Merge log files + run: bin/phpunit-merger log .Log/log/ .Log/junit.xml + + - name: Merge coverage files + run: bin/phpunit-merger coverage .Log/coverage/ .Log/coverage.xml + + - name: Archive PHPUnit logs + uses: actions/upload-artifact@v4 + with: + name: phpunit-logs-merged-${{ runner.os }}-${{ matrix.php }}-${{ matrix.coverage }} + path: .Log/* + retention-days: 1 diff --git a/.styleci.yml b/.styleci.yml index 4954816..4ba6e76 100644 --- a/.styleci.yml +++ b/.styleci.yml @@ -2,9 +2,12 @@ risky: true preset: psr12 +disabled: + - binary_operator_at_least_one_space + enabled: - alpha_ordered_imports - - binary_operator_spaces + - binary_operator_exactly_one_space - blank_line_before_return - hash_to_slash_comment - linebreak_after_opening_tag diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d911a22..0000000 --- a/.travis.yml +++ /dev/null @@ -1,98 +0,0 @@ -language: php - -dist: bionic - -addons: - apt: - packages: - - parallel - sonarcloud: - organization: "ichhabrecht-github" - branches: - - master - - pre-merge - -cache: - directories: - - $HOME/.composer/cache - - $HOME/.sonar/cache - -jdk: - - oraclejdk8 - -sudo: required - -before_install: - - echo 'xdebug.mode=coverage' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini - -install: - - > - for COVERAGE in $COVERAGE_VERSIONS; do - echo; - echo "Installing phpunit/php-code-coverage version $COVERAGE"; - echo; - echo; - git clean -Xdf; - composer require phpunit/php-code-coverage="$COVERAGE"; - git checkout composer.json; - mkdir -p .Log/coverage/ .Log/log/; - VERSION=${COVERAGE//[!0-9]/}; - - echo; - echo "Running phpunit"; - echo; - echo; - .Build/bin/phpunit --log-junit .Log/log/junit_$VERSION.xml --coverage-php .Log/coverage/coverage_$VERSION.cov --coverage-filter src/ tests/; - done - -script: - - > - echo; - echo "Merging log and coverage files"; - echo; - echo; - bin/phpunit-merger coverage .Log/coverage/ .Log/coverage.xml; - bin/phpunit-merger log .Log/log/ .Log/junit.xml; - - - > - echo; - echo "Running php lint"; - echo; - echo; - find . -name \*.php ! -path "./.Build/*" | parallel --gnu php -d display_errors=stderr -l {} > /dev/null \;; - -jobs: - fast_finish: true - include: - - stage: test - php: 8.1.0 - env: COVERAGE_VERSIONS="~10.0.0 ~10.1.0" - - stage: test - php: 8.1.0 - env: COVERAGE_VERSIONS="~9.0.0 ~9.1.0 ~9.2.0" - - stage: test - php: 8.0 - env: COVERAGE_VERSIONS="~9.0.0 ~9.1.0 ~9.2.0" - - - stage: ✔ with sonarqube scanner - if: type = push AND branch IN (master, pre-merge) AND env(SONAR_TOKEN) IS present AND fork = false - php: 8.1 - env: COVERAGE_VERSIONS="~10.0.0 ~10.1.0" - before_script: - script: - - > - echo; - echo "Merging log and coverage files"; - echo; - echo; - bin/phpunit-merger coverage .Log/coverage/ .Log/coverage.xml; - bin/phpunit-merger log .Log/log/ .Log/junit.xml; - - - git fetch --unshallow || true - - - > - echo; - echo "Running SonarQube Scanner"; - echo; - echo; - sonar-scanner; diff --git a/ChangeLog b/ChangeLog index 432746c..30af43c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2024-08-05 [RELEASE] Release of phpunit-merger 2.0.1 (Nicole Cordes) +2024-01-26 790ea36 Allow Symfony ^7.0 as dependency (Kennard Vermeiren) +2024-08-05 15ca05c [TASK] Remove Travis CI configuration (Nicole Cordes) +2024-08-05 9d32123 [TASK] Increase log files (Nicole Cordes) +2024-08-05 722e1eb [FEATURE] Add merge testing job (Nicole Cordes) +2024-08-04 261efdf [FEATURE] Add major version processing (Nicole Cordes) +2024-08-04 6ee2a5e [BUGFIX] Streamline PHPUnit configuration (Nicole Cordes) +2024-08-03 d193bd5 [BUGFIX] Re-add compatibility to phpunit/php-code-coverage ^9.0 (Nicole Cordes) +2024-08-03 f096c24 [FEATURE] Run PHPUnit tests and create artifacts (Nicole Cordes) +2024-08-02 1522a51 [FEATURE] Compute matrix values (Nicole Cordes) +2024-08-02 a715056 [FEATURE] Introduce GitHub Actions PHP linting (Nicole Cordes) +2024-08-02 4b361a0 [BUGFIX] Fix StyleCI configuration (Nicole Cordes) + 2023-10-02 [RELEASE] Release of phpunit-merger 2.0.0 (Nicole Cordes) 2023-10-02 ff24d85 [FEATURE] Add support for phpunit/php-code-coverage ^10.0 (Nicole Cordes) diff --git a/README.md b/README.md index 3e3041d..1f79d9e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # Merge multiple PHPUnit reports into one file [![Latest Stable Version](https://img.shields.io/packagist/v/nimut/phpunit-merger.svg)](https://packagist.org/packages/nimut/phpunit-merger) -[![StyleCI](https://styleci.io/repos/114540931/shield?branch=master)](https://styleci.io/repos/114540931) +[![StyleCI](https://styleci.io/repos/114540931/shield?branch=main)](https://styleci.io/repos/114540931) +![GitHub Actions](https://github.com/Nimut/phpunit-merger/actions/workflows/test.yml/badge.svg?event=push) Sometimes it is necessary to run multiple PHPUnit instances to execute all tests of a project. Unfortunately each run writes its own coverage and log reports. There is no support in PHPUnit to merge the reports of multiple runs. diff --git a/composer.json b/composer.json index fce6316..d07b978 100644 --- a/composer.json +++ b/composer.json @@ -34,14 +34,15 @@ "ext-dom": "*", "ext-json": "*", "ext-simplexml": "*", - "phpunit/php-code-coverage": "^9.0 || ^10.0|| ^11.0 || ^12.0", + "phpunit/php-code-coverage": "^11.0 || ^12.0", "symfony/console": ">=2.7 <8.0", "symfony/finder": ">=2.7 <8.0" }, "require-dev": { - "phpunit/phpunit": "^9.3 || ^10.0 || ^11.0 || ^12.0", + "phpunit/phpunit": "^11.0 || ^12.0", "symfony/filesystem": ">=2.7 <8.0", - "phpspec/prophecy": "^1.0" + "phpspec/prophecy": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.4" }, "suggest": { "friendsofphp/php-cs-fixer": "Tool to automatically fix PHP coding standards issues" diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 066b3e7..3a2bd2b 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -2,12 +2,7 @@ + executionOrder="depends,defects"> tests/PhpunitMerger/Command/Coverage diff --git a/src/PhpunitMerger/Command/CoverageCommand.php b/src/PhpunitMerger/Command/CoverageCommand.php index a010cf6..ffc90e9 100644 --- a/src/PhpunitMerger/Command/CoverageCommand.php +++ b/src/PhpunitMerger/Command/CoverageCommand.php @@ -5,6 +5,7 @@ namespace Nimut\PhpunitMerger\Command; use SebastianBergmann\CodeCoverage\CodeCoverage; +use SebastianBergmann\CodeCoverage\Driver\Driver; use SebastianBergmann\CodeCoverage\Driver\Selector; use SebastianBergmann\CodeCoverage\Filter; use SebastianBergmann\CodeCoverage\Report\Clover; @@ -53,7 +54,7 @@ protected function configure() ); } - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $finder = new Finder(); $finder->files() @@ -85,6 +86,12 @@ private function getCodeCoverage() { $filter = new Filter(); + if (method_exists(Driver::class, 'forLineCoverage')) { + $driver = Driver::forLineCoverage($filter); + + return new CodeCoverage($driver, $filter); + } + return new CodeCoverage((new Selector())->forLineCoverage($filter), $filter); } diff --git a/src/PhpunitMerger/Command/LogCommand.php b/src/PhpunitMerger/Command/LogCommand.php index d30e6ef..8f5b2ff 100644 --- a/src/PhpunitMerger/Command/LogCommand.php +++ b/src/PhpunitMerger/Command/LogCommand.php @@ -38,7 +38,7 @@ protected function configure() ); } - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $finder = new Finder(); $finder->files()