diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..ef4466c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_style = space +indent_size = 4 + +[Makefile] +indent_style = tab +indent_size = 8 + +[{*.yml,*.yaml}] +indent_style = space +indent_size = 2 diff --git a/.github/.editorconfig b/.github/.editorconfig new file mode 100644 index 0000000..e3ed7d1 --- /dev/null +++ b/.github/.editorconfig @@ -0,0 +1,3 @@ +[*.yml] +indent_style = space +indent_size = 2 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..a88b281 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @yoanm diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..a5538c7 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: + - package-ecosystem: composer + directory: "/" + schedule: + interval: monthly + open-pull-requests-limit: 10 diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..e7f4ef6 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,240 @@ +name: 'CI' +on: # Build any PRs and main branch changes + workflow_dispatch: # Allows to run the workflow manually from the Actions tab + pull_request: + types: + - opened + - edited + - synchronize + push: + branches: [ master ] + schedule: + - cron: '0 0 1 */6 *' # Every 6 months + +concurrency: + group: "${{ github.workflow }}-${{ github.head_ref || github.ref }}" + cancel-in-progress: true + +env: + TEST_OUTPUT_STYLE: pretty + CODACY_CACHE_PATH: ~/.cache/codacy + CODACY_BIN: ~/.cache/codacy/codacy.sh + +jobs: + tests: + name: UTs & FTs - PHPUnit ${{ matrix.phpunit-version }} + runs-on: ubuntu-latest + env: + COVERAGE_TYPE: none + strategy: + fail-fast: true + max-parallel: 4 + matrix: + include: + # Bare minimum => Lowest versions allowed by composer config + - phpunit-version: '9.0' + php-version: '8.0' + composer-flag: --prefer-lowest + # Up to date versions => Latest versions allowed by composer config + - phpunit-version: '9.0' + php-version: '8.2' + # Late phpunit migration => Lowest phpunit version with latest minor php version allowed by composer config + - phpunit-version: '9.0' + php-version: '8.2' + composer-flag: --prefer-lowest + # Late php migration => Latest phpunit version with lowest minor php version allowed by composer config + - phpunit-version: '9.0' + php-version: '8.0' + steps: + - name: Check out code + uses: actions/checkout@v3 + + - name: Enable coverage + if: ${{ matrix.php-version == '8.2' }} + run: | + echo "COVERAGE_OUTPUT_STYLE=clover" >> $GITHUB_ENV + echo "COVERAGE_TYPE=xdebug" >> $GITHUB_ENV + + - name: Setup PHP ${{ matrix.php-version }} + uses: shivammathur/setup-php@v2 + with: + php-version: '${{ matrix.php-version }}' + tools: composer + coverage: xdebug # Coverage driver is mandatory to be able to use/cover StrictCoverageListener + env: + # Always use latest available patch for the version + update: true + + - name: Setup cache + id: cache + uses: actions/cache@v3 + with: + path: | + ~/.composer + ./vendor + ${{ env.CODACY_CACHE_PATH }} + build/behat-code-coverage-cache + # Clear the cache if composer json (as composer.lock is in the repo) has been updated + key: tests-${{ matrix.php-version }}-${{ matrix.phpunit-version }}-${{ matrix.composer-flag }}-${{ hashFiles('composer.json') }} + + - name: Download codacy binary + if: steps.cache.outputs.cache-hit != 'true' + run: | + mkdir -p ${{ env.CODACY_CACHE_PATH }} \ + && curl -LN https://coverage.codacy.com/get.sh -o ${{ env.CODACY_BIN }} \ + && chmod +x ${{ env.CODACY_BIN }} \ + && ${{ env.CODACY_BIN }} download + + - name: Build + run: | + composer require -W ${{ env.COMPOSER_OPTIONS }} ${{ matrix.composer-flag }} \ + phpunit/phpunit:^${{ matrix.phpunit-version }} \ + && composer update ${{ matrix.composer-flag }} \ + && make build + + - name: Tests + run: make test-unit && make test-functional + + # Upload to codacy first as codecov action always remove coverage files despite move_coverage_to_trash at false + # And only if it's not a PR from a fork => Can't work as codacy secret is not accessible in that context + - name: Upload coverages to Codacy + if: ${{ github.event.pull_request.head.repo.full_name == 'yoanm/PhpUnitExtended' && env.COVERAGE_TYPE == 'xdebug' }} + run: ${{ env.CODACY_BIN }} report -r build/coverage-phpunit/unit.clover -r build/coverage-behat/clover.xml -r build/coverage-phpunit/functional.clover -t ${{ secrets.CODACY_PROJECT_TOKEN }} --partial + + # See the reports at https://codecov.io/gh/yoanm/PhpUnitExtended + - name: Upload unit tests coverage to codecov + if: ${{ env.COVERAGE_TYPE == 'xdebug' }} + uses: codecov/codecov-action@v3 + with: + file: "build/coverage-phpunit/unit.clover" + name: "unit-tests-${{ matrix.php-version }}-${{ matrix.phpunit-version }}" + flags: "unit-tests,php-${{ matrix.php-version }},sf-${{ matrix.phpunit-version }}" + fail_ci_if_error: true + move_coverage_to_trash: false + verbose: ${{ runner.debug == '1' }} + + - name: Upload functional tests coverage to codecov + if: ${{ env.COVERAGE_TYPE == 'xdebug' }} + uses: codecov/codecov-action@v3 + with: + files: "build/coverage-behat/clover.xml,build/coverage-phpunit/functional.clover" + name: "functional-tests-${{ matrix.php-version }}-${{ matrix.phpunit-version }}" + flags: "functional-tests,php-${{ matrix.php-version }},sf-${{ matrix.phpunit-version }}" + fail_ci_if_error: true + move_coverage_to_trash: false + verbose: ${{ runner.debug == '1' }} + + static-checks: + name: Static checks + runs-on: ubuntu-latest + needs: [ tests ] + steps: + - uses: actions/checkout@v3 + + - name: Setup PHP 8.2 + uses: shivammathur/setup-php@v2 + with: + php-version: 8.2 # Latest supported + tools: composer + coverage: none + env: + # Always use latest available patch for the version + update: true + + - name: Setup cache + id: cache + uses: actions/cache@v3 + with: + path: | + ~/.composer + # Clear the cache if composer json (as composer.lock is in the repo) has been updated + key: tests-${{ env.PHP_VERSION }}-${{ hashFiles('composer.json') }} + + - name: Build + run: make build + + - name: ComposerRequireChecker + uses: docker://webfactory/composer-require-checker:3.2.0 + + - name: Dependencies check + if: ${{ github.event_name == 'pull_request' }} + uses: actions/dependency-review-action@v1 + + finalize-codacy-coverage-report: + runs-on: ubuntu-latest + name: Finalize Codacy coverage report + if: ${{ github.event.pull_request.head.repo.full_name == 'yoanm/PhpUnitExtended' }} + needs: [ tests ] + steps: + - name: Setup cache + id: cache + uses: actions/cache@v3 + with: + path: | + ${{ env.CODACY_CACHE_PATH }} + key: codacy-final + + - name: Download codacy binary + if: steps.cache.outputs.cache-hit != 'true' + run: | + mkdir -p ${{ env.CODACY_CACHE_PATH }} \ + && curl -LN https://coverage.codacy.com/get.sh -o ${{ env.CODACY_BIN }} \ + && chmod +x ${{ env.CODACY_BIN }} \ + && ${{ env.CODACY_BIN }} download + + - name: Finalize reporting + run: ${{ env.CODACY_BIN }} final -t ${{ secrets.CODACY_PROJECT_TOKEN }} + + nightly-tests: + name: Nightly - PHPUnit ${{ matrix.phpunit-version }} + runs-on: ubuntu-latest + env: + COMPOSER_OPTIONS: '--optimize-autoloader --ignore-platform-req=php+' + continue-on-error: true + needs: [ static-checks, tests ] + strategy: + fail-fast: false + max-parallel: 4 + matrix: + php-version: + - '8.3' # Current php dev version + phpunit-version: + #- '9.0' # Lowest supported + - '9.0' # Latest supported + include: + - phpunit-version: '10' # Next PHPunit major version to manage with latest supported PHP version + php-version: '8.2' + + steps: + - name: Check out code + uses: actions/checkout@v3 + + - name: Setup PHP ${{ matrix.php-version }} + uses: shivammathur/setup-php@v2 + with: + php-version: '${{ matrix.php-version }}' + tools: composer + coverage: none + env: + # Always use latest available patch for the version + update: true + + - name: Setup cache + id: cache + uses: actions/cache@v3 + with: + path: | + ~/.composer + ./vendor + # Clear the cache if composer json (as composer.lock is in the repo) has been updated + key: tests-${{ matrix.php-version }}-${{ matrix.phpunit-version }}-${{ hashFiles('composer.json') }} + + - name: Build + run: | + composer require -W ${{ env.COMPOSER_OPTIONS }} \ + phpunit/phpunit:^${{ matrix.phpunit-version }} \ + && composer update \ + && make build + + - name: Test + run: make test-unit && make test-functional diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 58694ea..bf5054e 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -11,22 +11,27 @@ build_failure_conditions: build: dependencies: override: - - make build + - command: make build + title: Build deps + idle_timeout: 240 tests: stop_on_failure: true override: - - php-scrutinizer-run - - - command: make codestyle - analysis: - file: 'build/reports/cs-data' - format: 'php-cs-checkstyle' - - - command: make coverage - idle_timeout: 1200 - coverage: - file: 'build/coverage/clover.xml' - format: 'php-clover' + - command: make codestyle + title: Code style + - command: make scrutinizer-phpunit + idle_timeout: 1200 + coverage: + file: 'build/coverage-phpunit/scrutinizer.xml' + format: 'php-clover' + - command: make scrutinizer-behat + idle_timeout: 1200 + coverage: + file: 'build/coverage-behat/clover.xml' + format: 'php-clover' + - command: php-scrutinizer-run --enable-security-analysis + title: Scrutinizer checks + cache: directories: - ~/.composer @@ -36,13 +41,13 @@ build: variables: CI: 'true' TEST_OUTPUT_STYLE: 'pretty' + COMPOSER_OPTIONS: '--optimize-autoloader' COVERAGE_OUTPUT_STYLE: 'clover' COVERAGE_CLOVER_FILE_PATH: 'build/coverage/clover.xml' - PHPCS_REPORT_STYLE: 'checkstyle' - PHPCS_REPORT_FILE: 'build/reports/cs-data' - COMPOSER_OPTIONS: '--optimize-autoloader' php: - version: "7.1" + version: "8.2" + ini: + memory_limit: "-1" timezone: UTC postgresql: false redis: false diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 313dc02..0000000 --- a/.travis.yml +++ /dev/null @@ -1,26 +0,0 @@ -language: php - -php: - - '7.1' - - '7.2' - -env: - TEST_OUTPUT_STYLE: 'pretty' - PHPCS_REPORT_STYLE: 'full' - COMPOSER_OPTIONS: '--optimize-autoloader' - -sudo: false - -matrix: - fast_finish: true - -install: - - make build -script: - - make test-technical - - make test-functional - -cache: - directories: - - $HOME/.composer - - vendor diff --git a/Makefile b/Makefile index 887b2ae..0fccfed 100644 --- a/Makefile +++ b/Makefile @@ -1,19 +1,20 @@ COLOR_ENABLED ?= true TEST_OUTPUT_STYLE ?= dot -COVERAGE_OUTPUT_STYLE ?= html -CI_ONLY_SYMFONY_EXTENSION ?=false ## DIRECTORY AND FILE BUILD_DIRECTORY ?= build -REPORTS_DIRECTORY ?= ${BUILD_DIRECTORY}/reports -COVERAGE_DIRECTORY ?= ${BUILD_DIRECTORY}/coverage -COVERAGE_CLOVER_FILE_PATH ?= ${COVERAGE_DIRECTORY}/clover.xml +REPORTS_DIRECTORY ?= ${BUILD_DIRECTORY}/reports # Codestyle +BEHAT_COVERAGE_DIRECTORY ?= ${BUILD_DIRECTORY}/coverage-behat +PHPUNIT_COVERAGE_DIRECTORY ?= ${BUILD_DIRECTORY}/coverage-phpunit +PHPUNIT_UNIT_COVERAGE_FILE_PATH ?= ${PHPUNIT_COVERAGE_DIRECTORY}/unit.clover +PHPUNIT_FUNCTIONAL_COVERAGE_FILE_PATH ?= ${PHPUNIT_COVERAGE_DIRECTORY}/functional.clover ## Commands options ### Composer #COMPOSER_OPTIONS= ### Phpcs PHPCS_REPORT_STYLE ?= full +PHPCS_DISABLE_WARNING ?= "false" #PHPCS_REPORT_FILE= #PHPCS_REPORT_FILE_OPTION= @@ -38,66 +39,76 @@ else BEHAT_OUTPUT_STYLE_OPTION ?= --format progress endif -ifeq ("${COVERAGE_OUTPUT_STYLE}","clover") - PHPUNIT_COVERAGE_OPTION ?= --coverage-clover ${COVERAGE_CLOVER_FILE_PATH} -else +# Coverage driver is mandatory to be able to use/cover StrictCoverageListener +export XDEBUG_MODE=coverage +ifdef COVERAGE_OUTPUT_STYLE ifeq ("${COVERAGE_OUTPUT_STYLE}","html") - PHPUNIT_COVERAGE_OPTION ?= --coverage-html ${COVERAGE_DIRECTORY} - else - PHPUNIT_COVERAGE_OPTION ?= --coverage-text - endif + PHPUNIT_COVERAGE_OPTION ?= --coverage-html ${PHPUNIT_COVERAGE_DIRECTORY} + PHPUNIT_FUNCTIONAL_COVERAGE_OPTION ?= --coverage-html ${PHPUNIT_COVERAGE_DIRECTORY} + BEHAT_COVERAGE_OPTION ?= --profile coverage-html + else ifeq ("${COVERAGE_OUTPUT_STYLE}","clover") + PHPUNIT_COVERAGE_OPTION ?= --coverage-clover ${PHPUNIT_UNIT_COVERAGE_FILE_PATH} + PHPUNIT_FUNCTIONAL_COVERAGE_OPTION ?= --coverage-clover ${PHPUNIT_FUNCTIONAL_COVERAGE_FILE_PATH} + BEHAT_COVERAGE_OPTION ?= --profile coverage-clover + else + PHPUNIT_COVERAGE_OPTION ?= --coverage-text + PHPUNIT_FUNCTIONAL_COVERAGE_OPTION ?= --coverage-text + BEHAT_COVERAGE_OPTION ?= --profile coverage + endif endif ifneq ("${PHPCS_REPORT_FILE}","") PHPCS_REPORT_FILE_OPTION ?= --report-file=${PHPCS_REPORT_FILE} endif -ifeq ("${SCRUTINIZER}","true") - # On scrutinizer coverage must be done in all files - PHPUNIT_COVERAGE_GROUP_OPTION= +ifneq ("${PHPCS_DISABLE_WARNING}","true") + PHPCS_DISABLE_WARNING_OPTION= +else + PHPCS_DISABLE_WARNING_OPTION=-n endif + ## Project build (install and configure) build: install configure ## Project installation install: -ifeq ("${CI_ONLY_SYMFONY_EXTENSION}","true") - composer require symfony/dependency-injection ${COMPOSER_COLOR_OPTION} ${COMPOSER_OPTIONS} --prefer-dist --no-suggest --no-interaction -endif composer install ${COMPOSER_COLOR_OPTION} ${COMPOSER_OPTIONS} --prefer-dist --no-suggest --no-interaction ## project Configuration configure: # Project tests -test: - make test-functional - make test-technical - make codestyle +test: test-functional test-unit codestyle -test-technical: - ./vendor/bin/phpunit ${PHPUNIT_GROUP_OPTION} ${PHPUNIT_COLOR_OPTION} ${PHPUNIT_OUTPUT_STYLE_OPTION} --testsuite technical +ifdef PHPUNIT_COVERAGE_OPTION +test-unit: create-build-directories +endif +test-unit: + ./vendor/bin/phpunit ${PHPUNIT_COLOR_OPTION} ${PHPUNIT_OUTPUT_STYLE_OPTION} ${PHPUNIT_COVERAGE_OPTION} --testsuite technical +ifdef BEHAT_COVERAGE_OPTION +test-functional: create-build-directories +else ifdef PHPUNIT_FUNCTIONAL_COVERAGE_OPTION +test-functional: create-build-directories +endif test-functional: - ./vendor/bin/phpunit ${PHPUNIT_GROUP_OPTION} ${PHPUNIT_COLOR_OPTION} ${PHPUNIT_OUTPUT_STYLE_OPTION} --testsuite functional - ./vendor/bin/behat ${BEHAT_COLOR_OPTION} ${BEHAT_OUTPUT_STYLE_OPTION} ${BEHAT_TAG_OPTION} --no-snippets + ./vendor/bin/phpunit ${PHPUNIT_COLOR_OPTION} ${PHPUNIT_OUTPUT_STYLE_OPTION} ${PHPUNIT_FUNCTIONAL_COVERAGE_OPTION} --testsuite functional + ./vendor/bin/behat ${BEHAT_COLOR_OPTION} ${BEHAT_OUTPUT_STYLE_OPTION} ${BEHAT_COVERAGE_OPTION} --no-snippets -codestyle: create-reports-directory - ./vendor/bin/phpcs --standard=phpcs.xml.dist ${PHPCS_COLOR_OPTION} ${PHPCS_REPORT_FILE_OPTION} --report=${PHPCS_REPORT_STYLE} +codestyle: create-build-directories + ./vendor/bin/phpcs ${PHPCS_DISABLE_WARNING_OPTION} --standard=phpcs.xml.dist ${PHPCS_COLOR_OPTION} ${PHPCS_REPORT_FILE_OPTION} --report=${PHPCS_REPORT_STYLE} -coverage: create-coverage-directory - ./vendor/bin/phpunit ${PHPUNIT_COVERAGE_GROUP_OPTION} ${PHPUNIT_COLOR_OPTION} ${PHPUNIT_OUTPUT_STYLE_OPTION} ${PHPUNIT_COVERAGE_OPTION} +scrutinizer-phpunit: + XDEBUG_MODE=coverage ./vendor/bin/phpunit ${PHPUNIT_COLOR_OPTION} ${PHPUNIT_OUTPUT_STYLE_OPTION} --coverage-clover build/coverage-phpunit/scrutinizer.xml +scrutinizer-behat: + XDEBUG_MODE=coverage ./vendor/bin/behat ${BEHAT_COLOR_OPTION} ${BEHAT_OUTPUT_STYLE_OPTION} --profile coverage-clover --no-snippets # Internal commands -create-coverage-directory: - mkdir -p ${COVERAGE_DIRECTORY} - -create-reports-directory: - mkdir -p ${REPORTS_DIRECTORY} - +create-build-directories: + mkdir -p ${PHPUNIT_COVERAGE_DIRECTORY} ${BEHAT_COVERAGE_DIRECTORY} ${REPORTS_DIRECTORY} -.PHONY: build install configure test test-technical test-functional codestyle coverage create-coverage-directory create-reports-directory +.PHONY: build install configure test test-unit test-functional codestyle create-build-directories scrutinizer-behat scrutinizer-phpunit .DEFAULT: build diff --git a/behat.yml b/behat.yml index a5ce152..85d3979 100644 --- a/behat.yml +++ b/behat.yml @@ -1,5 +1,30 @@ default: + extensions: + DVDoug\Behat\CodeCoverage\Extension: + filter: + include: + directories: + 'src': ~ + reports: [] # No reports suites: default: contexts: - Tests\Functional\BehatContext\FeatureContext: ~ +coverage: + extensions: + DVDoug\Behat\CodeCoverage\Extension: + reports: + text: + showColors: true +coverage-html: + extensions: + DVDoug\Behat\CodeCoverage\Extension: + reports: + html: + target: build/coverage-behat +coverage-clover: + extensions: + DVDoug\Behat\CodeCoverage\Extension: + reports: + clover: + target: build/coverage-behat/clover.xml diff --git a/composer.json b/composer.json index 7d27cce..279aada 100644 --- a/composer.json +++ b/composer.json @@ -27,50 +27,15 @@ } }, "require": { - "php": ">=7.1", - "phpunit/phpunit": "~7.0" + "php": "^8.0", + "phpunit/phpunit": "^9.0", + "phpunit/php-code-coverage": "^9.2.4" }, "require-dev": { - "squizlabs/php_codesniffer": "3.*", + "squizlabs/php_codesniffer": "^3.5", "behat/behat": "~3.0", - "symfony/process": "~3.0" - }, - "scripts": { - "build": [ - "@composer install --prefer-dist --optimize-autoloader --ansi --no-suggest --no-interaction" - ], - "build-ci": [ - "mkdir -p build/coverage", - "@composer install --prefer-dist --ansi --no-suggest --no-interaction" - ], - "ci": [ - "rm composer.lock 2>/dev/null | true", - "@build-ci", - "@cs", - "@test" - ], - "test": [ - "@t-test", - "@f-test" - ], - "f-test": [ - "./vendor/bin/phpunit --testsuite functional", - "@behat" - ], - "t-test": [ - "./vendor/bin/phpunit --testsuite technical" - ], - "behat": [ - "cd app_test && ../vendor/bin/behat --no-snippets --colors --format progress" - ], - "cs": [ - "./vendor/bin/phpcs" - ], - "coverage": [ - "./vendor/bin/phpunit --coverage-html build/coverage" - ], - "coverage-clover": [ - "./vendor/bin/phpunit --coverage-clover build/coverage/clover.xml" - ] + "symfony/process": "^5.4", + "dvdoug/behat-code-coverage": "^5.0", + "phpspec/prophecy-phpunit": "^2.0" } } diff --git a/features/bootstrap/FeatureContext.php b/features/bootstrap/FeatureContext.php index 039a820..0f4a4c5 100644 --- a/features/bootstrap/FeatureContext.php +++ b/features/bootstrap/FeatureContext.php @@ -4,7 +4,6 @@ use Behat\Behat\Context\Context; use Behat\Behat\Tester\Exception\PendingException; use Symfony\Component\Process\Process; -use Symfony\Component\Process\ProcessBuilder; /** * Defines application features from the specific context. @@ -54,9 +53,12 @@ public function iRunPhpunitTestSuite() sprintf('%s/%s/vendor/bin/phpunit', __DIR__, '../..') ]; $argList = array_merge($baseArgList, $this->getCustomConfig()); - $processBuilder = new ProcessBuilder($argList); - $this->process = $processBuilder->getProcess(); + $env = []; + if (true === $this->withCoverage) { + $env['XDEBUG_MODE'] = 'coverage'; + } + $this->process = new Process($argList, null, $env); $this->process->run(); } diff --git a/src/Listener/RiskyToFailedListener.php b/src/Listener/RiskyToFailedListener.php index 4fd0e5c..5c5b7e0 100644 --- a/src/Listener/RiskyToFailedListener.php +++ b/src/Listener/RiskyToFailedListener.php @@ -41,7 +41,7 @@ public function addRiskyTest(Test $test, \Throwable $e, float $time) : void protected function addErrorIfNeeded(Test $test, \Throwable $e, $time) { /* Must be TestCase instance to have access to "getTestResultObject" method */ - if ($test instanceof TestCase) { + if ($test instanceof TestCase && $test->getTestResultObject() !== null) { $reason = $this->getErrorReason($e); if (null !== $reason) { $test->getTestResultObject()->addFailure( @@ -87,12 +87,13 @@ protected function getErrorReason(\Throwable $e) } elseif (preg_match('#This test did not perform any assertions#', $e->getMessage())) { /* beStrictAboutTestsThatDoNotTestAnything="true" (no specific exception) */ $reason = 'No test that do not test anything'; - } elseif (preg_match('#Trying to @cover or @use not existing #', $e->getMessage())) { + } elseif (preg_match('#"@covers [^"]+" is invalid#', $e->getMessage())) { /* forceCoversAnnotation="true" (no specific exception) */ $reason = 'Only executed code must be defined with @covers and @uses annotations'; } break; } + return $reason; } } diff --git a/src/Listener/StrictCoverageListener.php b/src/Listener/StrictCoverageListener.php index d13842b..9f6060e 100644 --- a/src/Listener/StrictCoverageListener.php +++ b/src/Listener/StrictCoverageListener.php @@ -35,7 +35,7 @@ protected function removeCoverageFor(TestCase $test) $coverage = $test->getTestResultObject()->getCodeCoverage(); if (null !== $coverage) { $id = $test->toString(); - $data = $coverage->getData(); + $data = $coverage->getData()->lineCoverage(); foreach ($data as $fileName => $lineData) { foreach ($lineData as $lineNumber => $testIdList) { if (is_array($testIdList)) { @@ -47,7 +47,7 @@ protected function removeCoverageFor(TestCase $test) } } } - $coverage->setData($data); + $coverage->getData()->setLineCoverage($data); } } } diff --git a/tests/Functional/Listener/DelegatingListenerTest.php b/tests/Functional/Listener/DelegatingListenerTest.php index 7d8ee14..132c269 100644 --- a/tests/Functional/Listener/DelegatingListenerTest.php +++ b/tests/Functional/Listener/DelegatingListenerTest.php @@ -7,6 +7,7 @@ use PHPUnit\Framework\TestListener; use PHPUnit\Framework\TestSuite; use PHPUnit\Framework\Warning; +use Prophecy\PhpUnit\ProphecyTrait; use Prophecy\Prophecy\ObjectProphecy; use Yoanm\PhpUnitExtended\Listener\DelegatingListener; @@ -15,10 +16,12 @@ */ class DelegatingListenerTest extends TestCase { + use ProphecyTrait; + /** @var DelegatingListener */ private $listener; - public function setUp() + public function setUp(): void { $this->listener = new DelegatingListener(); } @@ -64,14 +67,14 @@ public function testShouldHandleWarning() /** @var Test|ObjectProphecy $test */ $test = $this->prophesize(Test::class); /** @var Warning|ObjectProphecy $exception */ - $warning = $this->prophesize(Warning::class); + $warning = new Warning(); $this->listener->addListener($delegate->reveal()); - $delegate->addWarning($test->reveal(), $warning->reveal(), $time) + $delegate->addWarning($test->reveal(), $warning, $time) ->shouldBeCalled(); - $this->listener->addWarning($test->reveal(), $warning->reveal(), $time); + $this->listener->addWarning($test->reveal(), $warning, $time); } public function testShouldHandleFailure() diff --git a/tests/Functional/Listener/RiskyToFailedListenerTest.php b/tests/Functional/Listener/RiskyToFailedListenerTest.php index bbad623..7e13ec8 100644 --- a/tests/Functional/Listener/RiskyToFailedListenerTest.php +++ b/tests/Functional/Listener/RiskyToFailedListenerTest.php @@ -1,15 +1,15 @@ listener = new RiskyToFailedListener(); } @@ -28,36 +30,19 @@ public function setUp() public function testShouldHandleBadCoverageTagWarning() { $time = 0.3; + $test = new TestCaseMock(); + $message = '"@covers AppTest\DefaultClass::notExistingMethod" is invalid'; + $warning = new Warning($message); - /** @var TestCase|ObjectProphecy $test */ - $test = $this->prophesize(TestCase::class); - /** @var TestResult|ObjectProphecy $testResult */ - $testResult = $this->prophesize(TestResult::class); - /** @var Warning $warning */ - $warning = new Warning( - 'Trying to @cover or @use not existing method "AppTest\DefaultClass::notExistingMethod".' - ); - - $test->getTestResultObject() - ->willReturn($testResult->reveal()) - ->shouldBeCalled(); + $test->setTestResultObject(new TestResult()); - $testResult->addFailure( - $test, - Argument::allOf( - Argument::type(AssertionFailedError::class), - Argument::that(function (AssertionFailedError $arg) { - return preg_match( - '#Only executed code must be defined with @covers and @uses annotations#', - $arg->getMessage() - ); - }) - ), - $time - ) - ->shouldBeCalledTimes(1); + $this->listener->addWarning($test, $warning, $time); - $this->listener->addWarning($test->reveal(), $warning, $time); + $failures = $test->getTestResultObject()->failures(); + $this->assertCount(1, $failures); + $failure = array_shift($failures); + $this->assertInstanceOf(TestFailure::class, $failure); + $this->assertStringContainsString($message, $failure->getExceptionAsString()); } /** @@ -71,36 +56,26 @@ public function testShouldHandleBadCoverageTagWarning() public function testShouldHandleRiskyTestWith( $exceptionClass, $expectedReason, - $called = true, - $exceptionMessage = null + $called, + $exceptionMessage = 'my default exception message' ) { $time = 0.3; - - /** @var TestCase|ObjectProphecy $test */ - $test = $this->prophesize(TestCase::class); - /** @var TestResult|ObjectProphecy $testResult */ - $testResult = $this->prophesize(TestResult::class); - /** @var \Exception|ObjectProphecy $exception */ + $test = new TestCaseMock(); $exception = new $exceptionClass($exceptionMessage); - if ($exception instanceof AssertionFailedError) { - $test->getTestResultObject() - ->willReturn($testResult->reveal()) - ->shouldBeCalled(); - } - $testResult->addFailure( - $test, - Argument::allOf( - Argument::type(AssertionFailedError::class), - Argument::that(function (AssertionFailedError $arg) use ($expectedReason) { - return preg_match(sprintf('#%s#', preg_quote($expectedReason)), $arg->getMessage()); - }) - ), - $time - ) - ->shouldBeCalledTimes($called ? 1 : 0); + $test->setTestResultObject(new TestResult()); - $this->listener->addRiskyTest($test->reveal(), $exception, $time); + $this->listener->addRiskyTest($test, $exception, $time); + + $failures = $test->getTestResultObject()->failures(); + if ($called) { + $this->assertCount(1, $failures); + $failure = array_shift($failures); + $this->assertInstanceOf(TestFailure::class, $failure); + $this->assertStringContainsString($exceptionMessage, $failure->getExceptionAsString()); + } else { + $this->assertCount(0, $failures); + } } /** @@ -112,10 +87,12 @@ public function getExceptionMessageProvider() 'Output exception' => [ 'exceptionClass' => OutputError::class, 'expectedMessage' => 'No output during test', + 'called' => true, ], 'Coverage exception' => [ 'exceptionClass' => UnintentionallyCoveredCodeError::class, 'expectedMessage' => 'Executed code must be defined with @covers and @uses annotations', + 'called' => true, ], 'Globals manipulation - globals' => [ 'exceptionClass' => RiskyTestError::class, diff --git a/tests/Functional/Listener/StrictCoverageListenerTest.php b/tests/Functional/Listener/StrictCoverageListenerTest.php index dd133e0..d6a9345 100644 --- a/tests/Functional/Listener/StrictCoverageListenerTest.php +++ b/tests/Functional/Listener/StrictCoverageListenerTest.php @@ -4,8 +4,11 @@ use PHPUnit\Framework\OutputError; use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestResult; +use Prophecy\PhpUnit\ProphecyTrait; use SebastianBergmann\CodeCoverage\CodeCoverage; +use SebastianBergmann\CodeCoverage\Driver\Xdebug3Driver; use SebastianBergmann\CodeCoverage\Filter; +use SebastianBergmann\CodeCoverage\ProcessedCodeCoverageData; use Tests\Functional\Mock\TestCaseMock; use Yoanm\PhpUnitExtended\Listener\StrictCoverageListener; @@ -14,12 +17,14 @@ */ class StrictCoverageListenerTest extends TestCase { + use ProphecyTrait; + /** @var StrictCoverageListener */ private $listener; const TEST_FILENAME = 'file_name'; - public function setUp() + public function setUp(): void { $this->listener = new StrictCoverageListener(); } @@ -33,15 +38,10 @@ public function testShouldCorrectlyHandleCoverageRemovalWith(array $baseCoverage { $time = 0.3; - /** @var TestCaseMock $test */ - $test = new TestCaseMock('test_name'); - /** @var TestResult $testResult */ + $test = new TestCaseMock(); $testResult = new TestResult(); - /** @var Filter $filter */ - $filter = new Filter('./plop', 'plop', 'plop'); - /** @var CodeCoverage $coverage */ - $coverage = new CodeCoverage(null, $filter); - /** @var OutputError $exception */ + $filter = new Filter(); + $coverage = new CodeCoverage(new Xdebug3Driver($filter), $filter); $exception = new OutputError(); @@ -50,11 +50,9 @@ public function testShouldCorrectlyHandleCoverageRemovalWith(array $baseCoverage $testCoverageData = $baseCoverageData; $testCoverageData[self::TEST_FILENAME][0][] = $test->toString(); - $coverage->setData($testCoverageData); - - // Mandatory, else $coverage->getData() will return an empty array (see internal behavior) - $filter->addFileToWhitelist('plop.plop'); - //$coverage->setAddUncoveredFilesFromWhitelist(false); + $processed = new ProcessedCodeCoverageData(); + $processed->setLineCoverage($testCoverageData); + $coverage->setData($processed); $this->listener->addRiskyTest($test, $exception, $time); @@ -62,7 +60,7 @@ public function testShouldCorrectlyHandleCoverageRemovalWith(array $baseCoverage if (!isset($expectedCoverageData[self::TEST_FILENAME][0])) { $expectedCoverageData[self::TEST_FILENAME][0] = []; } - $this->assertSame($expectedCoverageData, $coverage->getData(true)); + $this->assertEquals($expectedCoverageData, $coverage->getData(true)->lineCoverage()); } /** diff --git a/tests/Functional/Listener/YoanmTestsStrategyListenerTest.php b/tests/Functional/Listener/YoanmTestsStrategyListenerTest.php index a16e656..be0f0af 100644 --- a/tests/Functional/Listener/YoanmTestsStrategyListenerTest.php +++ b/tests/Functional/Listener/YoanmTestsStrategyListenerTest.php @@ -2,6 +2,7 @@ namespace Tests\Functional\Listener; use PHPUnit\Framework\TestCase; +use Prophecy\PhpUnit\ProphecyTrait; use Yoanm\PhpUnitExtended\Listener\RiskyToFailedListener; use Yoanm\PhpUnitExtended\Listener\StrictCoverageListener; use Yoanm\PhpUnitExtended\Listener\YoanmTestsStrategyListener; @@ -13,12 +14,14 @@ */ class YoanmTestsStrategyListenerTest extends TestCase { + use ProphecyTrait; + /** @var YoanmTestsStrategyListener */ private $listener; const TEST_FILENAME = 'file_name'; - public function setUp() + public function setUp(): void { $this->listener = new YoanmTestsStrategyListener(); }